Estandarización de CPUE
CPUE es una de las métricos mas comunes para estimando el estado de poblaciones de peces.
La idea claramente es básico: si estas capturando menos pescado con el mismo esfuerzo, probablemente hay menos peses para capturar.
Pero, es útil pensar un poco mas en lo que nos dice CPUE en realidad.
Pensamos en una ecuación básico de captura:
\[ captura = biomasa * esfuerzo * capturabilidad \]
Por ejemplo, si hay 100 peses en un lago (biomasa), pesco por una hora con pintado (esfuerzo), y tengo una 10% probabilidad de capturar cualquier peses encuentran my carnada sobre la hora (capturabilidad), voy a capturar 10 peses. Si le población de peses baja a 50, entonces voy a capturar 5 peses.
Por esta relación, podemos decir que
\[ \frac{capture}{esfuerzo} = biomasa * capturabilidad \]
o mas clara…
\[ CPUE = qB \]
Entonces si B sube o baja, CPUE sube o baja.
La problema aquí es que si tenemos ańos de CPUE datos, y tratamos de inferir el estado de la población (B) por la tendencias del CPUE.
Se puede ver que si CPUE cambia sobre tiempo, hay dos posibilidades que pueden explicar esto:
biomasa esta cambiando o** q esta cambiando**
Ejemplo
Suponga que encontramos una pesquería nuevo.
Queremos monitorear el progreso y sostenibilidad de este pesquería sobre tiempo, y para hacer esto colectamos CPUE sobre tiempo.
Si tecnología es constante, CPUE es una señal perfecto de biomasa

En este caso, la abilidad de las flotas son constantes
cpue_example %>%
filter(creep == 'none') %>%
ggplot(aes(year,fleet_q, color = fleet, linetype = fleet)) +
geom_line(size = 1.5) +
facet_wrap(~creep) +
pres_theme

Pero, suponga que hay dos flotas, una artesanal y otro comercial.
Sobre tiempo, la flota artesanal use la misma tecnología.
Pero, la flota comercial cada año usa un poco de sus ganancias para comprar mejores barcos, mejores redes, etc.
El resultado, la flota comercial cada año mejora su capacidad de pesca
cpue_example %>%
filter(creep == 'some') %>%
select(year,fleet_cpue,fleet_q,fleet,creep) %>%
ggplot(aes(year,fleet_q, color = fleet, linetype = fleet)) +
geom_line(size = 1.5) +
facet_wrap(~creep) +
pres_theme

El resultado de esto es que CPUE ya no es una señal imparcial de biomasa.
Un gerente, mirando solo CPUE, pensarías que biomasa estaba creciendo en los primeros años, y quizás tomara decisiones incorrectos por resultado.
cpue_example %>%
group_by(year, creep) %>%
summarise(biomass = mean(b, na.rm = T), mean_cpue = mean(fleet_cpue, na.rm = T),
median_cpue = median(fleet_cpue, na.rm = T)) %>%
gather(metric,value, biomass,mean_cpue) %>%
group_by(creep, metric) %>%
mutate(value = value/value[year == 1]) %>%
ggplot(aes(year,value, color = metric, linetype = metric)) +
geom_line(size = 1.5) +
facet_wrap(~creep) +
scale_y_continuous(name = 'Porcentaje de nivel inicial', labels = percent) +
pres_theme

Como podemos corregir esto?
Usando Regresión para Estandarización
El proceso de “estandarización” nos puede ayudar con este problema.
El objetivo de estandarización es de controlar por factores como tecnología que podrían cesgar CPUE, y sacar la señal verdadero de la población en CPUE.
Suponga que no sabemos exactamente que esta pasando con este cambio de tecnología.
Pero, sabemos que hay dos flotas, y uno tiene mejor tecnología del otro.
Podemos controlar por esto, y ver que este proceso mejora nuestro habilidad de usar CPUE pare monitorear biomasa.
cpue_example <- cpue_example %>%
mutate(log_cpue = log(fleet_cpue))
nocreep_reg <- lm(log_cpue ~ factor(year), data = cpue_example %>% filter(creep == 'none'))
creep_reg <- lm(log_cpue ~ factor(year) , data = cpue_example %>% filter(creep == 'some'))
full_creep_reg <- lm(log_cpue ~ factor(year) + fleet, data = cpue_example %>% filter(creep == 'some'))
# summary(nocreep_reg)
#
# summary(creep_reg)
#
# summary(full_creep_reg)
termfun <- function(x) {
out = as.numeric(paste(x, collapse = ''))
return(out)
}
tidy_nocreep <- tidy(nocreep_reg) %>%
mutate(year_term = str_detect(term,'year')) %>%
mutate(trans_term = exp(estimate + std.error^2/2)) %>%
filter(year_term == T) %>%
mutate(year = str_extract_all(term,'[\\d]', simplify = F),
year = map_dbl(year,termfun))
tidy_creep <- tidy(creep_reg) %>%
mutate(year_term = str_detect(term,'year')) %>%
mutate(trans_term = exp(estimate + std.error^2/2)) %>%
filter(year_term == T) %>%
mutate(year = str_extract_all(term,'[\\d]', simplify = F),
year = map_dbl(year,termfun))
tidy_fullcreep <- tidy(full_creep_reg) %>%
mutate(year_term = str_detect(term,'year')) %>%
mutate(trans_term = exp(estimate + std.error^2/2)) %>%
filter(year_term == T) %>%
mutate(year = str_extract_all(term,'[\\d]', simplify = F),
year = map_dbl(year,termfun),
rel_trans_term = trans_term/trans_term[year == min(year)])
cpue_example %>%
filter(creep == 'some') %>%
group_by(year, creep) %>%
summarise(biomass = mean(b, na.rm = T), mean_cpue = mean(fleet_cpue, na.rm = T),
median_cpue = median(fleet_cpue, na.rm = T)) %>%
gather(metric,value, biomass,mean_cpue) %>%
group_by(creep, metric) %>%
mutate(value = value/value[year == 1]) %>%
ggplot(aes(year,value)) +
geom_line(size = 1.5, aes(color = metric, linetype = metric)) +
facet_wrap(~creep) +
scale_y_continuous(name = 'Porcentaje de nivel inicíal', labels = percent) +
geom_point(data = tidy_fullcreep, aes(year,rel_trans_term)) +
pres_theme

Nuestro objetivo por el resto de este ejercicio es aprender como hacer esto nosotros mismos.
Primeros Pasos: Datos “Tidy” (Ordenado)
Antes de explorar los datos de CPUE, vamos a aprender algunos herramientas para organización y uso de datos.
La caja de herramientas esta contenido en tidyverse
install.packages('tidyverse')
Científicos de datos, según entrevistas y estimaciones expertos, pasen entre 50%-80% de su tiempo atascado en el trabajo mundano de colectando y preparando datos, antes que puede ser usado para resultados útiles. - NYTimes (2014)
El objetivo de los herramientas que vamos a hablar hoy es hacer este proceso mas fácil y rápido.
Recursos
Puedes descargar RStudio si no lo tienes, o quieres sacar la versión mas reciente 0.99.903 (menu RStudio -> About RStudio), que tiene muchos herramientas útiles.
También, puedes sacar la versión de R mas recién aquí.
Porque usar dplyr y tidyr?
- Velocidad
- Legibilidad
- Integración con
ggplot2
Que es “tidy data”?
Esto es en ejemplo clásico de “messy data”:
Es útil y claro (mas o menos) para seres humanos
Pero, para una computadora, es muy confuso
Usando tidy data
Vamos a aprender como usar R para construir datos tidy.
Datos ordenados tienen una filosofía básico:
Cada columna es un tipo de datos, cada fila es una observación
Esto no sueno muy complicado, pero es muy poderoso
peces <- read.csv(file = 'data/datos_desordenados.csv', stringsAsFactors = F)
as_data_frame(peces)
Que son los problemas con estos datos?
Vamos a usar algunos herramientas tel paquete dplyr para resolver esto.
Para empezar, voy a introducir tu nuevo mejor amigo: el “pipe” (tubo) operador
El tubo (%>%) es como un corredor de relais
La idea is que toma la cosa a la izquierda, y lo pasa a la derecha
Suponga que tenemos una vector de datos a = c(1,2,3)
Como obtenemos la suma de a?
sum(a)
a <- c(1,2,3)
sum(a)
[1] 6
Podemos usar el tubo para esto
a %>%
sum()
[1] 6
Tenemos a, lo pasamos a %>%, y %>% lo pasa a sum
Esto no parece muy útil en este case, pero su poder crece muy rápidamente.
- Ayuda mucho con legibilidad
- Ayuda con memoria
Retornamos a nuestros datos de peses. Nuestro objetivo es convertirlo a un formato ordenado.
peces
tidyr tiene una función llamado gather que nos ayuda aquí.
gather(peces,especia, captura, contains('pes'))
Vemos que gather nos ayudo crear un data_frame tidy.
Por que es mejor esto?
Podemos hacer este mismo operación usando tubos
peces %>%
gather(especia,captura, contains('pes'))
Ya que tenemos datos ordenados, podemos hacer operaciones.
Supongo que queremos captura total sobre todos especias en cada año. Como hacemos eso?
dplyr nos ayuda también!
peces %>%
gather(especia,captura, contains('pes')) %>%
group_by(year) %>%
summarise(captura_total = sum(captura, na.rm = T))
Que paso aquí?
group_by hace grupos de los datos, y hace operaciones en estos grupos. Podemos agregar por cualquier tipo de datos categóricos
summarize hace resúmenes de los datos en cada grupo, usando cualquier operación que quieres.
Suponga que queremos el total y la captura máxima en cada año.
peces %>%
gather(especia,captura, contains('pes')) %>%
group_by(year) %>%
summarise(captura_total = sum(captura),
maximo_captura = max(captura))
Hay dos tipos generales de operaciones en dplyr
summarise colapsa datos
mutate modifica datos
Suponga que queremos calcular el porcentaje de capturas por cada especia de pes en cada año.
Podemos usar mutate para esto
peces %>%
gather(especia,captura, contains('pes')) %>%
group_by(year) %>%
mutate(captura_annual = sum(captura)) %>%
arrange(year) %>%
mutate(porcentaje = captura / captura_annual)
Y entonces, suponga que queremos calcular el mínimo porcentaje de algún especia en cada año
foo <- function(x,y){
out = mean(exp(x))
return(out)
}
peces %>%
gather(especia,captura, contains('pes')) %>%
group_by(year) %>%
mutate(captura_annual = sum(captura)) %>%
arrange(year) %>%
mutate(porcentaje = captura / captura_annual) %>%
summarise(min_porcentaje = min(porcentaje))
Ahora, también tenemos un base de datos de temperatura de agua en cada año
Queremos explorar si algún relación entre capturas y temperatura
Como hacemos eso?
walk<-vector(length = length(unique(peces$year)))
walk[1]<- rnorm(1,0,.75)
for (i in 2:length(unique(peces$year)))
{
walk[i]<- walk[i - 1] * 1 + rnorm(1,0,0.75)
}
water_temp <- data_frame(year = unique(peces$year), temperatura = walk + 18 )
water_temp %>%
ggplot(aes(year, temperatura)) +
geom_point(size = 2) +
xlab('Año') +
ylab('Temperatura') +
pres_theme

Un opción es usar el mando left_join
left_join use un clave para identificar observaciones comunes en diferentes bases de datos, y apenda cada observación que tiene un pareja a tu base de datos original.
Miramos
peces %>%
gather(especia,captura, contains('pes')) %>%
left_join(water_temp, by = 'year')
Ahora, podemos investigar nuestro hipótesis que temperatura y captura son correlacionado
peces %>%
gather(especia,captura, contains('pes')) %>%
left_join(water_temp, by = 'year') %>%
group_by(year) %>%
summarise(captura_total = sum(captura),
temperatura = mean(temperatura)) %>%
ggplot(aes(captura_total, temperatura)) +
geom_point() +
geom_smooth(method = 'lm') +
geom_hline(aes(yintercept = mean(temperatura))) + pres_theme

Estos herramientas son la fundación de código eficiente.
Después de esto, vamos a ver como connector los manipulaciones de dplyr con los poderes visuales de ggplot
capturas <- read_csv(file = 'data/capturas.csv')
capturas <- capturas %>%
gather(`1950`:`2012`, key = 'year', value = 'capturas') %>%
mutate(capturas = as.numeric(capturas),
year = as.numeric(year))
Ejercicios de Datos “Tidy”
- Importe los datos “capturas.csv” usando
read_csv
- Convierte los datos a un formato “tidy”
- Examine los datos, que tipo de datos son cada columna?
- Examine los capturas en particular
- Use
mutate para convertir los capturas a calores numéricos
- Que paso a los valores de captura?
- Use
group_by y summarize para calcular la captura total en cada año en cada país
- Use otros herramientas para explorar los datos
Gráficos con ggplot
Ya que hemos tenido un introducción rápido a datos “tidy”, vamos a ver como podemos visualizar nuestros resultados usando un paquete poderoso llamado ggplot2
El “gg” en ggplot representa “gramática de gráficos”
Es un formato diseñado para hablar con datos tidy para hacer gráficos buenos, reproducible, y flexible
No hay nada en ggplot que no so puede hacer con los herramientas gráficos base de R. Por lo mismo, puedes hacer todos los operaciones de datos tidy en base R también.
Pero, la filosofía de estos paquetes es estandarizar el proceso para hacer lo mas rápido, mas transparente, y mas reproducible.
Empezamos on gráficos de ggplot, jugando con los datos de captura que creemos en el ultimo paso.
capturas
Queremos hacer un gráfico de capturas totales sobre tiempo.
Primero, podemos usar dplyr para sacar estos datos
capturas_por_tiempo <- capturas %>%
group_by(year) %>%
summarise(capturas_totales = sum(capturas, na.rm = T))
capturas_por_tiempo
Como hacemos este gráfico normalmente?
plot(capturas_por_tiempo$year, capturas_por_tiempo$capturas_totales)

No hay nada malo con esto. Pero, vamos a ver como hacer esto con ggplot
Empezamos creando una cuadra vacía
ggplot(data = capturas_por_tiempo, aes(x = year, y = capturas_totales))

El mando ggplot cree nuestro cuadro, donde vamos a pintar con diferentes capas.
Añadimos capas usando el símbolo +. Esto nos dice que queremos añadir una capa a nuestro cuadro.
ggplot(data = capturas_por_tiempo, aes(x = year, y = capturas_totales)) +
geom_point()

Ya tenemos algo muy similar a lo que tenemos usando plot, pero se ve mucho mas confundido!
Empezamos con explicando los compartimientos aquí
aes representa estética (aesthetics). Esto es done pones las cosas que quieres dibujar. Puedes ver que esto es lo mismo que plot(x,y)
Por que no necesitas usar $?
Entonces,
ggplot(data = capturas_por_tiempo, aes(x = year, y = capturas_totales))
Dice usa los datos capturas_por_tiempo, y hace un gráfico con coordinados x de “year” y coordinados y de “capturas_totales”
geom_point añade una capa de “scatterplot” a nuestro cuadro
geom_ representa diferentes tipos de capas, por ejemplo
geom_point añade una capa de “scatterplot” a nuestro cuadro
geom_line añade una capa de linea a nuestro cuadro
ggplot(data = capturas_por_tiempo, aes(x = year, y = capturas_totales)) +
geom_point() +
geom_line()

Esto tiene algunos aventadas sobre gráficos básicos.
Hace tu código mas legible y linear
Pero, a este punto parece mas trabajo que nomas usando plot
El poder verdadero empieza com la habilidad de fácilmente visualizar diferentes atributos de los datos.
Supone que queremos hacer el tamaño de los círculos proporcional a la captura total en ese año
ggplot(data = capturas_por_tiempo, aes(x = year, y = capturas_totales)) +
geom_point(aes(size = capturas_totales)) +
geom_line()

Y con solo eso, hemos añadido un visualizaron y una leyenda!
ggplot interpreta cosas adentro de aes como atributos que debe dar leyenda. Puedes usar diferentes tipos de cosas, incluyendo por ejemplo tamaño, color, o forma.
Notas que puse el comanda size en el aes de geom_point
Que pasa si lo pongo en ggplot?
ggplot(data = capturas_por_tiempo, aes(x = year, y = capturas_totales, size = capturas_totales)) +
geom_point() +
geom_line()

aes adentro de ggplot es compartido con todos capas.
aes adentro den un geom_ es solo para ese capa
ggplot(data = capturas_por_tiempo, aes(x = year, y = capturas_totales, color = capturas_totales)) +
geom_point() +
geom_line()

Que pasa si ponemos color afuera de aes? Esto es como hacemos colores sin asociaciones
ggplot(data = capturas_por_tiempo, aes(x = year, y = capturas_totales) ) +
geom_point(color = 'red') +
geom_line()

Todavía hemos nomas asido datos mas claros. Como podemos usar ggplot para ver mas información?
Por ejemplo, queremos ver que tipos de especia componen los capturas totales. Regresamos a dplyr por un momento
capturas_por_tiempo <- capturas %>%
group_by(year, `Tipo de Especia`) %>%
summarise(capturas_totales = sum(capturas, na.rm = T))
capturas_por_tiempo
Que pasa si tratemos de visualizar esto?
capturas_por_tiempo %>%
ggplot(aes(year,capturas_totales)) +
geom_line()

Que paso??
Tenemos observaciones para cada especia, podemos visualizar eso por asociando el tipo de especia a un parámetro estético, como color.
capturas_por_tiempo %>%
ggplot(aes(year,capturas_totales)) +
geom_line(aes(color = `Tipo de Especia`))

es un poco confuso, pero puedes ver el poder! Para hacerlo mas claro, podemos por ejemplo hacer un barplot del ultimo año.
Hay mucho aquí, y hay cursos y libros, pero puedes ver el poder de usando datos “tidy” con ggplot
capturas_por_tiempo %>%
filter(year == max(year)) %>%
mutate(`Tipo de Especia` = fct_reorder(`Tipo de Especia`,capturas_totales)) %>%
ggplot(aes(`Tipo de Especia`,capturas_totales)) +
geom_bar(stat = 'identity', position = 'dodge') +
coord_flip()

Ejercicios de ggplot
- Examine los datos adentro del base de datos
diamonds
- Hace un gráfico usando
plot de price (precio) como un función de carat (peso)
- Hace el mismo gráfico usando
ggplot
- Use
ggplot para hacer los puntos el color rojo
- Use
ggplot para hacer los colores representativo a color
- Que nos dice este información?
- Hace los colores representativo a
color y el forma representativo de cut
- Juega con diferentes maneras de explorando la relación entre precio y características de los diamantes
Re-Introducción a Regresión
Ya tenemos algunos herramientas para ayudarnos jugar con datos en R.
Ahora, vamos a ver como podemos usar estos herramientas juntos con regresión para estandarizar CPUE
Para empezar, vamos a explorar regresión en R.
Regresión es una forma de modelo, y hay clases y clases en el tópico, que nosotros vamos a tratar de entender en muy poco tiempo.
Básicamente, una regresión es un modelo de la relación entre diferentes variables.
En teoría:
\[ efecto = causa + error \]
Mas técnicamente, generalmente hablamos en términos de datos “dependientes” (las cosas que estamos tratando de predecir), y “independientes” (las cosas que usamos para predecir)
\[ dependiente = independiente + error \]
Vamos a explorar este idea un poco con un base de datos sobre el precio y características de diamantes.
Supone que queremos invertir en diamantes. Vamos a tratar de usar estos datos para entender la relación entre precio de diamantes y sus características usando los datos que exploramos en el ejercicio anterior.
Empezamos con una cosa básico
data("diamonds")
diamantes <- diamonds %>%
as_data_frame() %>%
rename(depth_perc = depth) %>%
rename(len = x, width = y, depth = z) %>%
mutate(cut = factor(cut, ordered = F))
diamantes
Vamos a empezar por mirar unas relaciones, empezando con “carats” y precio
diamantes %>%
ggplot(aes(carat, price)) +
geom_point(aes()) +
ylab('Precio') +
scale_y_continuous(labels = dollar) +
pres_theme

Podemos ver que hay una relación, pero no es perfecto. Como podemos sacar este relación?
En excel, si te recuerdes, puedes poner un “fit line”, que nos da cosas como el \(R^{2}\) y la pendiente de una linea. Podemos sacar el mismo proceso en R usando la función lm (linear model)
lm us muy fácil usar
lm(dependiente ~ independiente, data = datos).
Que dice el modelo sobre la relación entre “carat” (peso) y precio?
r1 <- lm(price ~ carat, data = diamantes)
summary(r1)
Call:
lm(formula = price ~ carat, data = diamantes)
Residuals:
Min 1Q Median 3Q Max
-18585.3 -804.8 -18.9 537.4 12731.7
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -2256.36 13.06 -172.8 <2e-16 ***
carat 7756.43 14.07 551.4 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1549 on 53938 degrees of freedom
Multiple R-squared: 0.8493, Adjusted R-squared: 0.8493
F-statistic: 3.041e+05 on 1 and 53938 DF, p-value: < 2.2e-16
Ejercicios de Regresión
- Como interpretamos el coeficiente de
carat?
- Como interpretamos el
intercept?
- Como interpretamos el
R-squared?
Mas Regresión
Vemos que este modelo * nos da una estimación “significante” de la relación entre “carat” y precio. * También, vemos que el modelo explica ~85% de la variación de los datos, que es un nivel muy alto: si sabemos los “carats” de un diamante, podemos usar este modelo para hacer un buen estimación de su precio.
Pero, tenemos mas datos, y hay una teoría que dice que debemos incluir todos variables que creemos afectan nuestro variable dependiente. Como podemos usar estos otros variables?
Vemos por ejemplo que tenemos un variable que se llama “cut”. Esto nos dice la calidad de la cortada de el diamante. Claramente, esto va a afectar el precio. Pero, como podemos incluir este tipo de datos en una regresión?
diamantes
R nos deja usar este tipo de cosa, usando un clase de variable que se llama “factores” (factors). Factores son variables que representan grupos. Se puede crear factores de cualquier tipo de cosa, pero generalmente es cosas como, ciudades, país, especia, o en este caso, corte.
corte <- as.character(diamantes$cut)
head(corte)
[1] "Ideal" "Premium" "Good" "Premium" "Good"
[6] "Very Good"
En este caso, vemos una cuerda normal. Podemos convertir en un factor usando as.factor
head(as.factor(corte))
[1] Ideal Premium Good Premium Good Very Good
Levels: Fair Good Ideal Premium Very Good
Se ve similar, pero ahora es un factor, con niveles correspondiendo a corta. Es decir, que es data categórico.
Esto es útil por que ahora podemos usarlo en una regresión.
r_factor <- lm(price ~ (cut), data = diamantes)
summary(r_factor)
Call:
lm(formula = price ~ (cut), data = diamantes)
Residuals:
Min 1Q Median 3Q Max
-4258 -2741 -1494 1360 15348
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 4358.76 98.79 44.122 < 2e-16 ***
cutGood -429.89 113.85 -3.776 0.000160 ***
cutVery Good -377.00 105.16 -3.585 0.000338 ***
cutPremium 225.50 104.40 2.160 0.030772 *
cutIdeal -901.22 102.41 -8.800 < 2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 3964 on 53935 degrees of freedom
Multiple R-squared: 0.01286, Adjusted R-squared: 0.01279
F-statistic: 175.7 on 4 and 53935 DF, p-value: < 2.2e-16
head(diamantes$cut)
[1] Ideal Premium Good Premium Good Very Good
Levels: Fair Good Very Good Premium Ideal
Que paso a “fair?” Y como podemos usar caracteres como números?
R cree lo que llamamos “dummy variables”
a = diag(4) %>% as_data_frame()
colnames(a) <- c('Good','Very Good','Premium','Ideal')
a$cut = c('Good','Very Good','Premium','Ideal')
a
Podemos entonces usar datos numéricos (e.g. carat) y categórico (e.g. cut) en el mismo modelo
r2 <- lm(price ~ carat + cut, data = diamantes)
summary(r2)
Call:
lm(formula = price ~ carat + cut, data = diamantes)
Residuals:
Min 1Q Median 3Q Max
-17540.7 -791.6 -37.6 522.1 12721.4
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -3875.47 40.41 -95.91 <2e-16 ***
carat 7871.08 13.98 563.04 <2e-16 ***
cutGood 1120.33 43.50 25.75 <2e-16 ***
cutVery Good 1510.14 40.24 37.53 <2e-16 ***
cutPremium 1439.08 39.87 36.10 <2e-16 ***
cutIdeal 1800.92 39.34 45.77 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1511 on 53934 degrees of freedom
Multiple R-squared: 0.8565, Adjusted R-squared: 0.8565
F-statistic: 6.437e+04 on 5 and 53934 DF, p-value: < 2.2e-16
Como interpretamos este modelo?
Usando Regresión
Ahora tenemos este modelo, como podemos usarlo?
Vamos a usar el paquete broom
broom nos ayuda sacar información importante y útil de una regresión
summary nos da mucho información, pero es difícil usar.
Podemos usar glance para sacar resumes del modelo
glance(r2)
Podemos usar tidy para ver los coeficientes
tidy(r2)
Ahora tenemos coeficientes en una forma mucho mas útil para nosotros.
Como interpretamos los diferentes variables aquí?
Muchas veces, el propósito de un modelo es para hacer predicciones. Podemos hacer esto con la función predict
head(predict(r2))
1 2 3 4 5 6
-264.1968 -783.4653 -944.7890 -153.7787 -315.1024 -476.2746
plot(diamantes$price, predict(r2))

Otro opción us usar la función augment de el paquete broom
augment(r2)
Esto es un poco difícil de entender, lo que es mas importante es el variable .fitted, que con los valores previsto.
Diagnósticos
Creando un regresión es facil. Este es bueno pero también tiene riesgos.
Hay cursos y libros en este tópico, y no podemos cubrir todo hoy.
Pero, vamos a ver algunos diagnosticas básicos.
plot(r2)




Y, podemos ver que hay problemas. No indique que no podemos usar este modelo, pero dice que necesitamos investigar las cosas mas.
Para dar otro ejemplo, vamos a ver diagnósticos de otro base de datos sobre flores
r_iris <- lm(Sepal.Length ~ Petal.Length + Petal.Width + Species, data = iris)
summary(r_iris)
Call:
lm(formula = Sepal.Length ~ Petal.Length + Petal.Width + Species,
data = iris)
Residuals:
Min 1Q Median 3Q Max
-0.75238 -0.23089 -0.00211 0.23100 1.03108
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 3.682982 0.107403 34.291 < 2e-16 ***
Petal.Length 0.905946 0.074311 12.191 < 2e-16 ***
Petal.Width -0.005995 0.156260 -0.038 0.969
Speciesversicolor -1.598362 0.205706 -7.770 1.32e-12 ***
Speciesvirginica -2.112647 0.304024 -6.949 1.16e-10 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.3392 on 145 degrees of freedom
Multiple R-squared: 0.8367, Adjusted R-squared: 0.8322
F-statistic: 185.8 on 4 and 145 DF, p-value: < 2.2e-16
plot(r_iris)




En este case los diagnósticos son mucho mejor.
Usando Regresión Para CPUE
Ahora tenemos los herramientas para regresar a nuestro pregunta original: como podemos estandarizar los valores de CPUE para Chita?
Empezamos con los datos
chita_cpue <- read.csv(file = 'Chita_CPUE_2001_2015.csv', stringsAsFactors = F) %>%
as_data_frame()
chita_cpue
Vemos que no hay un variable llamado CPUE, tenemos que crearlo.
Con los variables que tenemos, que opciones tenemos para definir CPUE?
chita_cpue <- chita_cpue %>%
mutate(cpue = Chita_kg / No_Fishers)
Vamos a explorar la idea rápidamente aquí, y entonces tratar de hacer esto solo.
Creamos una regresión con variables dependiente cpue y variables dependiente Dist_coast, Vessel y Gear. Importantemente, también vamos a incluir el variable Year.
Year is la cosa mas importante aquí.
Recuerda que la idea de los coeficiente en una regresión es que el coeficiente es el efecto de este variable sobre el variable dependiente controlando por los otros variables.
Entonces, esto dice que los coeficientes de Year debe ser el efecto de año sobre CPUE, controlando en este caso por cosas como distancia de la costa.
Recuerda que el objetivo de de estandarización is crear una tendencia de abundancia imparcial.
Entonces, podemos interpretar los coeficientes de Year como la tendencia de abundancia, controlando para los otros variables.
cpue_reg <- lm(cpue ~ factor(Year) + Dist_coast + Vessel + Gear, data = chita_cpue)
summary(cpue_reg)
Call:
lm(formula = cpue ~ factor(Year) + Dist_coast + Vessel + Gear,
data = chita_cpue)
Residuals:
Min 1Q Median 3Q Max
-61.47 -4.27 -2.03 0.51 1197.09
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2.99424 2.65635 1.127 0.259666
factor(Year)2002 -0.12451 0.40121 -0.310 0.756302
factor(Year)2003 0.59862 0.43086 1.389 0.164733
factor(Year)2004 -2.02753 0.48466 -4.183 2.88e-05
factor(Year)2005 -2.54059 0.50842 -4.997 5.85e-07
factor(Year)2006 -2.58283 0.46877 -5.510 3.61e-08
factor(Year)2007 -1.84782 0.47679 -3.876 0.000107
factor(Year)2008 -1.53034 0.44029 -3.476 0.000510
factor(Year)2009 -0.79144 0.42099 -1.880 0.060122
factor(Year)2010 -2.14605 0.42571 -5.041 4.65e-07
factor(Year)2011 -2.27723 0.51873 -4.390 1.14e-05
factor(Year)2012 0.02159 0.42117 0.051 0.959113
factor(Year)2013 -0.63627 0.45634 -1.394 0.163242
factor(Year)2014 -1.51528 0.38678 -3.918 8.95e-05
factor(Year)2015 -1.65232 0.36442 -4.534 5.80e-06
Dist_coast 0.31921 0.04502 7.090 1.37e-12
VesselBOTE 1.97329 2.50629 0.787 0.431090
VesselCABALLITO DE TOTORA -2.25413 2.51482 -0.896 0.370079
VesselCANOA -5.52592 16.52199 -0.334 0.738035
VesselCHALANA -0.22967 2.50772 -0.092 0.927028
VesselLANCHA 5.85501 2.58535 2.265 0.023537
VesselNO DEFINIDO 0.15322 2.55868 0.060 0.952250
VesselPERSONA -1.59662 2.58558 -0.618 0.536903
VesselYATE 4.19103 3.07437 1.363 0.172822
VesselZAPATO -1.76983 3.02306 -0.585 0.558252
GearARRASTRE -6.12413 8.24284 -0.743 0.457508
GearATARRAYA 0.36650 5.51659 0.066 0.947031
GearBUCEO - COMPRESORA 2.47714 1.37469 1.802 0.071560
GearBUCEO - PULMONERO 3.74250 3.61350 1.036 0.300348
GearCERCO 42.25021 1.24691 33.884 < 2e-16
GearCHINCHORRO 4.72935 1.66102 2.847 0.004412
GearCORTINA/AGALLERA 2.85572 0.84369 3.385 0.000713
GearCORTINA/TRASMALLO 4.59305 0.88756 5.175 2.29e-07
GearESPINEL 0.87347 11.57723 0.075 0.939859
GearOTROS 5.16764 1.02223 5.055 4.32e-07
GearPINTA 1.79344 0.86347 2.077 0.037806
GearTRINCHE -1.34243 0.98686 -1.360 0.173740
(Intercept)
factor(Year)2002
factor(Year)2003
factor(Year)2004 ***
factor(Year)2005 ***
factor(Year)2006 ***
factor(Year)2007 ***
factor(Year)2008 ***
factor(Year)2009 .
factor(Year)2010 ***
factor(Year)2011 ***
factor(Year)2012
factor(Year)2013
factor(Year)2014 ***
factor(Year)2015 ***
Dist_coast ***
VesselBOTE
VesselCABALLITO DE TOTORA
VesselCANOA
VesselCHALANA
VesselLANCHA *
VesselNO DEFINIDO
VesselPERSONA
VesselYATE
VesselZAPATO
GearARRASTRE
GearATARRAYA
GearBUCEO - COMPRESORA .
GearBUCEO - PULMONERO
GearCERCO ***
GearCHINCHORRO **
GearCORTINA/AGALLERA ***
GearCORTINA/TRASMALLO ***
GearESPINEL
GearOTROS ***
GearPINTA *
GearTRINCHE
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 16.33 on 39832 degrees of freedom
Multiple R-squared: 0.1031, Adjusted R-squared: 0.1023
F-statistic: 127.2 on 36 and 39832 DF, p-value: < 2.2e-16
Vemos que ya tenemos los coeficientes para Year. Terminado?
Vamos a ver algunos de los diagnósticos.
plot(cpue_reg)




Que esta pasando aquí?
cpue_reg %>%
augment() %>%
ggplot() +
geom_vline(aes(xintercept = 0)) +
geom_histogram(aes(.fitted), bins = 50, alpha = 0.75) +
pres_theme

cpue_reg %>%
augment() %>%
ggplot() +
geom_vline(aes(xintercept = 0)) +
geom_histogram(aes(.resid), alpha = 0.75) +
scale_y_log10() +
pres_theme

Vemos que la distribución no es normal!
Corregimos esto usando logaritmos.
La problema es que datos de CPUE generalmente no son normalmente repartido.
Son lo que llamamos un distribución “log normal”
norm_var = rnorm(1000,5,10)
lnorm_var = rlnorm(1000,log(5),1)
data_frame('normal' = norm_var, 'log normal' = lnorm_var) %>%
gather('Distribución','value') %>%
ggplot(aes(value, fill = Distribución)) +
geom_vline(aes(xintercept = 0)) +
geom_histogram(color = 'black', bins = 50) +
facet_grid(`Distribución` ~.) +
pres_theme

Estos dos distribuciones tienen el mismo promedio, pero se ven muy diferente. Ves que
- La distribución normal es simétrico, y puede tener valores negativo
- La distribución log-normal no es simétrico, y no puede tener valores negativo
Mirando la histograma de los “residuals”, vemos que se parecen mucho mas “log normal”
Recuerda también que lm tiene la suposición que tus datos son normales, y podemos ver que los CPUE’s no son.
Podemos resolver esto por suposición los valores de cpue a log(cpue)
log_cpue_reg <- lm(log(cpue) ~ factor(Year) + Dist_coast + Vessel + Gear, data = chita_cpue)
Vemos los diagnósticos
log_cpue_reg %>%
augment() %>%
ggplot() +
geom_vline(aes(xintercept = 0)) +
geom_histogram(aes(.resid), alpha = 0.75) +
pres_theme

Mucho mejor!
plot(log_cpue_reg)




Ahora, podemos usar este modelo para sacar nuestro estandarizado estimaciones de abundancia.
Vamos a hacer esto usando broom otra vez.
coefs <- log_cpue_reg %>%
tidy()
coefs <- coefs %>%
bind_cols(confint_tidy(log_cpue_reg))
Casi estamos allí
Vemos que tenemos coeficientes para “Year”. Como podemos sacar lo?
Podemos usar el mando filter (filtro) para sacar filas que contienen “Year”
La cosa difícil aquí es como identificar cuales observaciones contienen “Year”
Podemos hacer esto manualmente. O, podemos usar un mando para encontrar todos estos entradas.
str_detect de el paquete stringr nos puede ayudar aquí.
abundancia <- coefs %>%
filter(str_detect(term,'Year')) %>%
mutate(year = str_extract_all(term,'[\\d]', simplify = F),
year = map_dbl(year,termfun)) %>%
ungroup() %>%
mutate(trans_est = exp(estimate + std.error^2/2), trans_lower = exp(conf.low + std.error^2/2), trans_upper = exp(conf.high + std.error^2/2 )) %>%
ungroup() %>%
mutate(rel_est = trans_est / trans_est[year == min(year)] - 1)
abundancia
Tienes que tener precaución con esto y verificar que solo sacaste lo que quieres, pero es un herramienta poderoso.
Ya tenemos indices de “abundancia”, relativo a 2001. Estos valores son nuestros indices estandarizado de CPUE.
cpue_trend <- chita_cpue %>%
ungroup() %>%
group_by(Year) %>%
summarise(median_cpue = median(cpue, na.rm = T)) %>%
mutate( delta_cpue = median_cpue / median_cpue[Year == min(Year) + 1] - 1)
abundancia %>%
ggplot() +
geom_pointrange(aes(year,estimate,ymin = conf.low, ymax = conf.high)) +
ylab('Abundancia') +
xlab('Año') +
pres_theme

Hay un paso final, que es un poco difícil explicar, pero es importante.
Los coeficientes que hemos estimado son le relación entre año y log(CPUE).
Tenemos que convertir estos valores a la relación entre año y CPUE.
Hacemos esto por la relación
\[ \beta_{normal} = exp(\beta_{log} + \frac{\sigma_{\beta}^{2}}{2}) \]
En este case, \(\beta\) es los coeficientes de año, y \(\sigma\) es el std.error sacado por tidy
abundancia %>%
select(term, estimate, std.error,trans_est)
abundancia %>%
mutate(year = str_extract_all(term,'[\\d]', simplify = F),
year = map_dbl(year,termfun)) %>%
ggplot() +
geom_pointrange(aes(year,trans_est,ymin = trans_lower, ymax = trans_upper)) +
ylab('Abundancia') +
xlab('Año') +
pres_theme

abundancia %>%
mutate(year = str_extract_all(term,'[\\d]', simplify = F),
year = map_dbl(year,termfun)) %>%
ggplot() +
geom_line(aes(year,rel_est, color = 'Estandarizado'), size = 2) +
geom_point( data = cpue_trend, aes(Year,delta_cpue, color = 'Observado'), size = 2) +
ylab('Abundancia') +
xlab('Año') +
scale_fill_discrete(name = 'Fuente') +
pres_theme

Y por fin! Tenemos un indice de abundancia estandarizado.
Se que eso fue mucho… En resumen
- Explorar tus datos
- Calcula CPUE
- Escoge tus variables independientes (los de la mano derecha), siempre incluyendo tiempo como un variable fijo
- Convertir CPUE a log(CPUE)
- use
lm o otro función para crear un regresión de log(cpue) ~ variables dependientes
- Examine los diagnosticas de la regresión usando
plot
- Si todo parece mas o menos bien, continua. Si parece raro, encuentra un estadístico
- Saca los coeficientes de tiempo (e.g. año)
- Convertir a unidades normales por la formula anterior
- Mira la tendencia generado
- Trata otro modelos, compara resultados, discutir
Ejercicios Finales
- Importa los datos de CPUE
- Escoge como quieres calcular CPUE
- Hace resumes, gráficos y tablas, de variables de interés
- Use tus variables escogido en un regresión
- Interpretar todos los diferentes coeficiente
- Saca los indices de abundancias estandarizado
- Hace gráficos de los resultados
- Construye por lo menos un modelo alternativo (usando otros variables independientes)
- Compare los resultados
LS0tCnRpdGxlOiAiRW50cmVuYW1pZW50byBkZSBDw7NkaWdvIHkgQ1BVRSIKYXV0aG9yOiAiRGFuIE92YW5kbyIKZGF0ZTogIkxpbWEgUGVydSwgMjAxNiIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBmaWdfcmV0aW5hOiBudWxsCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgZmlnX2NhcHRpb246IHllcwogICAgZmlnX3JldGluYTogbnVsbAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgcGRmX2RvY3VtZW50OgogICAgZmlnX2NhcHRpb246IHllcwogICAgdG9jOiB5ZXMKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBGLCBtZXNzYWdlID0gRiwgd2FybmluZyA9IEYpCmBgYAoKYGBge3IsIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBpbmNsdWRlID0gRn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZm9yY2F0cykKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShicm9vbSkKCnByZXNfdGhlbWUgPC10aGVtZV9idyhiYXNlX3NpemUgPSAxNiwgYmFzZV9mYW1pbHkgPSAnSGVsdmV0aWNhJykKCmBgYAoKCiMgRXN0YW5kYXJpemFjacOzbiBkZSBDUFVFCgpDUFVFIGVzIHVuYSBkZSBsYXMgbcOpdHJpY29zIG1hcyBjb211bmVzIHBhcmEgZXN0aW1hbmRvIGVsIGVzdGFkbyBkZSBwb2JsYWNpb25lcyBkZSBwZWNlcy4gCgpMYSBpZGVhIGNsYXJhbWVudGUgZXMgYsOhc2ljbzogc2kgZXN0YXMgY2FwdHVyYW5kbyBtZW5vcyBwZXNjYWRvIGNvbiBlbCBtaXNtbyBlc2Z1ZXJ6bywgcHJvYmFibGVtZW50ZSBoYXkgbWVub3MgcGVzZXMgcGFyYSBjYXB0dXJhci4KClBlcm8sIGVzIMO6dGlsIHBlbnNhciB1biBwb2NvIG1hcyBlbiBsbyBxdWUgbm9zIGRpY2UgQ1BVRSBlbiByZWFsaWRhZC4KClBlbnNhbW9zIGVuIHVuYSBlY3VhY2nDs24gYsOhc2ljbyBkZSBjYXB0dXJhOgoKJCQgY2FwdHVyYSA9IGJpb21hc2EgKiBlc2Z1ZXJ6byAqIGNhcHR1cmFiaWxpZGFkICQkCgpQb3IgZWplbXBsbywgc2kgaGF5IDEwMCBwZXNlcyBlbiB1biBsYWdvICgqYmlvbWFzYSopLCBwZXNjbyBwb3IgdW5hIGhvcmEgY29uIHBpbnRhZG8gKCplc2Z1ZXJ6byopLCB5IHRlbmdvIHVuYSAxMCUgcHJvYmFiaWxpZGFkIGRlIGNhcHR1cmFyIGN1YWxxdWllciBwZXNlcyBlbmN1ZW50cmFuIG15IGNhcm5hZGEgc29icmUgbGEgaG9yYSAoKmNhcHR1cmFiaWxpZGFkKiksIHZveSBhIGNhcHR1cmFyIDEwIHBlc2VzLiBTaSBsZSBwb2JsYWNpw7NuIGRlIHBlc2VzIGJhamEgYSA1MCwgZW50b25jZXMgdm95IGEgY2FwdHVyYXIgNSBwZXNlcy4KClBvciBlc3RhIHJlbGFjacOzbiwgcG9kZW1vcyBkZWNpciBxdWUKCiQkIFxmcmFje2NhcHR1cmV9e2VzZnVlcnpvfSA9IGJpb21hc2EgKiBjYXB0dXJhYmlsaWRhZCAkJAoKbyBtYXMgY2xhcmEuLi4KCiQkIENQVUUgPSBxQiAgJCQKCkVudG9uY2VzIHNpIEIgc3ViZSBvIGJhamEsICpDUFVFKiBzdWJlIG8gYmFqYS4gCgpMYSBwcm9ibGVtYSBhcXXDrSBlcyBxdWUgc2kgdGVuZW1vcyBhxYRvcyBkZSAqQ1BVRSogZGF0b3MsIHkgdHJhdGFtb3MgZGUgaW5mZXJpciBlbCBlc3RhZG8gZGUgbGEgcG9ibGFjacOzbiAoKkIqKSBwb3IgbGEgdGVuZGVuY2lhcyBkZWwgKkNQVUUqLgoKU2UgcHVlZGUgdmVyIHF1ZSBzaSBDUFVFIGNhbWJpYSBzb2JyZSB0aWVtcG8sIGhheSBkb3MgcG9zaWJpbGlkYWRlcyBxdWUgcHVlZGVuIGV4cGxpY2FyIGVzdG86IAoKKipiaW9tYXNhIGVzdGEgY2FtYmlhbmRvICoqbyoqICpxKiBlc3RhIGNhbWJpYW5kbyoqCgojIyBFamVtcGxvCgpgYGB7ciBDUFVFIGVqZW1wbG8sIGVjaG89Rn0KCmZsZWV0cyA8LSBkYXRhX2ZyYW1lKGVmZm9ydCA9IDEwLCBxID0gMSwgZmxlZXQgPSBjKCdhJywnYicpKQoKcG9wZnVuIDwtIGZ1bmN0aW9uKHIgPSAwLjIsSyA9IDEwMDAsZWZmb3J0ID0gNSxxID0gMC4wMSxxY3JlZXAgPSAxLCB5ZWFycyA9IDIwKXsKCiAgYiA8LSAgcmVwKE5BLCB5ZWFycykKCiAgcXNlcmllcyA8LSByZXAoTkEsIHllYXJzKQoKICBjYXRjaCA8LSByZXAoTkEsIHllYXJzKQoKICBmbGVldF9hIDwtIHJlcChOQSwgeWVhcnMpCgogIGZsZWV0X2IgPC0gcmVwKE5BLCB5ZWFycykKCiAgYlsxXSA8LSBLCgogIHFzZXJpZXNbMV0gPC0gcQoKICBmb3IgKHQgaW4gMjp5ZWFycykgewogIGNhdGNoW3QgLSAxXSA8LSBiW3QgLSAxXSAqIChlZmZvcnQgKiBxc2VyaWVzW3QgLSAxXSAgKyAgZWZmb3J0ICogcSkKCiAgZmxlZXRfYVt0IC0gMV0gPC0KICBjYXRjaFt0IC0gMV0gKiAoZWZmb3J0ICogcXNlcmllc1t0IC0gMV0pIC8gKGVmZm9ydCAqIHFzZXJpZXNbdCAtIDFdICArICBlZmZvcnQgKgogIHEpCgogIGZsZWV0X2JbdCAtIDFdIDwtCiAgY2F0Y2hbdCAtIDFdICogKGVmZm9ydCAqIHEpIC8gKGVmZm9ydCAqIHFzZXJpZXNbdCAtIDFdICArICBlZmZvcnQgKiBxKQoKICBiW3RdIDwtIGJbdCAtIDFdICsgYlt0IC0gMV0gKiByICogKDEgLSBiW3QgLSAxXSAvIEspIC0gIGNhdGNoW3QgLSAxXQoKICBxc2VyaWVzW3RdID0gcXNlcmllc1t0IC0gMV0gKiBxY3JlZXAKCiAgfQoKICBvdXQgPC0gZGF0YV9mcmFtZSgKICBiID0gYiwKICBxID0gcXNlcmllcywKICByID0gciwKICBLID0gSywKICBjYXRjaCA9IGNhdGNoLAogIGNwdWUgPSBjYXRjaCAvIGVmZm9ydCwKICBmbGVldF9hID0gZmxlZXRfYSwKICBmbGVldF9iID0gZmxlZXRfYiwKICB5ZWFyID0gMTp5ZWFycywKICBlZmZvcnQgPSBlZmZvcnQKICApICU+JQogICAgZ2F0aGVyKGZsZWV0LGZsZWV0X2NhdGNoLCBmbGVldF9hLCBmbGVldF9iKSAlPiUKICAgIG11dGF0ZShmbGVldF9jcHVlID0gZmxlZXRfY2F0Y2gvZWZmb3J0LCBmbGVldF9xID0gZmxlZXRfY3B1ZSAvIGIpCiAgcmV0dXJuKG91dCkKCn0KCm5vY3JlZXAgPC0gcG9wZnVuKCkgJT4lCiAgbXV0YXRlKGNyZWVwID0gJ25vbmUnKQoKc29tZWNyZWVwIDwtIHBvcGZ1bihxY3JlZXAgPSAxLjIpICU+JQogIG11dGF0ZShjcmVlcCA9ICdzb21lJykKCmNwdWVfZXhhbXBsZSA8LSBub2NyZWVwICU+JQogIGJpbmRfcm93cyhzb21lY3JlZXApCgoKYGBgCgpTdXBvbmdhIHF1ZSBlbmNvbnRyYW1vcyB1bmEgcGVzcXVlcsOtYSBudWV2by4KClF1ZXJlbW9zIG1vbml0b3JlYXIgZWwgcHJvZ3Jlc28geSBzb3N0ZW5pYmlsaWRhZCBkZSBlc3RlIHBlc3F1ZXLDrWEgc29icmUgdGllbXBvLCB5IHBhcmEgaGFjZXIgZXN0byBjb2xlY3RhbW9zIENQVUUgc29icmUgdGllbXBvLgoKU2kgdGVjbm9sb2fDrWEgZXMgY29uc3RhbnRlLCBDUFVFIGVzIHVuYSBzZcOxYWwgcGVyZmVjdG8gZGUgYmlvbWFzYQoKYGBge3IsIGVjaG89Rn0KY3B1ZV9leGFtcGxlICU+JQogIGZpbHRlcihjcmVlcCA9PSAnbm9uZScpICU+JQogIGdyb3VwX2J5KHllYXIsIGNyZWVwKSAlPiUKICBzdW1tYXJpc2UoYmlvbWFzcyA9IG1lYW4oYiwgbmEucm0gPSBUKSwgbWVhbl9jcHVlID0gbWVhbihmbGVldF9jcHVlLCBuYS5ybSA9IFQpLAogICAgICAgICAgICBtZWRpYW5fY3B1ZSA9IG1lZGlhbihmbGVldF9jcHVlLCBuYS5ybSA9IFQpKSAlPiUKICBnYXRoZXIobWV0cmljLHZhbHVlLCBiaW9tYXNzLG1lYW5fY3B1ZSkgJT4lCiAgZ3JvdXBfYnkoY3JlZXAsIG1ldHJpYykgJT4lCiAgbXV0YXRlKHZhbHVlID0gdmFsdWUvdmFsdWVbeWVhciA9PSAxXSkgJT4lCiAgZ2dwbG90KGFlcyh5ZWFyLHZhbHVlLCBjb2xvciA9IG1ldHJpYywgbGluZXR5cGUgPSBtZXRyaWMpKSArCiAgZ2VvbV9saW5lKHNpemUgPSAxLjUpICsKICBmYWNldF93cmFwKH5jcmVlcCkgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gJ1BvcmNlbnRhamUgZGUgbml2ZWwgaW5pY2lhbCcsIGxhYmVscyA9IHBlcmNlbnQpICsgCiAgcHJlc190aGVtZQpgYGAKCkVuIGVzdGUgY2FzbywgbGEgYWJpbGlkYWQgZGUgbGFzIGZsb3RhcyBzb24gY29uc3RhbnRlcwoKYGBge3IgdGVzdCwgZmlnLmNhcD0iUmVsYWNpb24gZGUgQ1BVRSB5IGJpb21hc2EgY29uIHRlY25vbG9naWEgY29uc3RhbnRlIn0KCiBjcHVlX2V4YW1wbGUgJT4lCiAgZmlsdGVyKGNyZWVwID09ICdub25lJykgJT4lCiAgZ2dwbG90KGFlcyh5ZWFyLGZsZWV0X3EsIGNvbG9yID0gZmxlZXQsIGxpbmV0eXBlID0gZmxlZXQpKSArCiAgZ2VvbV9saW5lKHNpemUgPSAxLjUpICsKICBmYWNldF93cmFwKH5jcmVlcCkgKwogIHByZXNfdGhlbWUKCmBgYAoKUGVybywgc3Vwb25nYSBxdWUgaGF5IGRvcyBmbG90YXMsIHVuYSBhcnRlc2FuYWwgeSBvdHJvIGNvbWVyY2lhbC4gCgpTb2JyZSB0aWVtcG8sIGxhIGZsb3RhIGFydGVzYW5hbCB1c2UgbGEgbWlzbWEgdGVjbm9sb2fDrWEuIAoKUGVybywgbGEgZmxvdGEgY29tZXJjaWFsIGNhZGEgYcOxbyB1c2EgdW4gcG9jbyBkZSBzdXMgZ2FuYW5jaWFzIHBhcmEgY29tcHJhciBtZWpvcmVzIGJhcmNvcywgbWVqb3JlcyByZWRlcywgZXRjLiAKCkVsIHJlc3VsdGFkbywgKipsYSBmbG90YSBjb21lcmNpYWwgY2FkYSBhw7FvIG1lam9yYSBzdSBjYXBhY2lkYWQgZGUgcGVzY2EqKgoKCmBgYHtyfQoKIGNwdWVfZXhhbXBsZSAlPiUKICBmaWx0ZXIoY3JlZXAgPT0gJ3NvbWUnKSAlPiUKICBzZWxlY3QoeWVhcixmbGVldF9jcHVlLGZsZWV0X3EsZmxlZXQsY3JlZXApICU+JQogIGdncGxvdChhZXMoeWVhcixmbGVldF9xLCBjb2xvciA9IGZsZWV0LCBsaW5ldHlwZSA9IGZsZWV0KSkgKwogIGdlb21fbGluZShzaXplID0gMS41KSArCiAgZmFjZXRfd3JhcCh+Y3JlZXApICsgCiAgcHJlc190aGVtZQoKYGBgCgpFbCByZXN1bHRhZG8gZGUgZXN0byBlcyBxdWUgQ1BVRSB5YSBubyBlcyB1bmEgc2XDsWFsIGltcGFyY2lhbCBkZSBiaW9tYXNhLgoKVW4gZ2VyZW50ZSwgbWlyYW5kbyBzb2xvIENQVUUsIHBlbnNhcsOtYXMgcXVlIGJpb21hc2EgZXN0YWJhIGNyZWNpZW5kbyBlbiBsb3MgcHJpbWVyb3MgYcOxb3MsIHkgcXVpesOhcyB0b21hcmEgZGVjaXNpb25lcyBpbmNvcnJlY3RvcyBwb3IgcmVzdWx0YWRvLgoKYGBge3IsIGZpZy5jYXA9J0RvcyBmbG90YXMnfQoKCmNwdWVfZXhhbXBsZSAlPiUKICBncm91cF9ieSh5ZWFyLCBjcmVlcCkgJT4lCiAgc3VtbWFyaXNlKGJpb21hc3MgPSBtZWFuKGIsIG5hLnJtID0gVCksIG1lYW5fY3B1ZSA9IG1lYW4oZmxlZXRfY3B1ZSwgbmEucm0gPSBUKSwKICAgICAgICAgICAgbWVkaWFuX2NwdWUgPSBtZWRpYW4oZmxlZXRfY3B1ZSwgbmEucm0gPSBUKSkgJT4lCiAgZ2F0aGVyKG1ldHJpYyx2YWx1ZSwgYmlvbWFzcyxtZWFuX2NwdWUpICU+JQogIGdyb3VwX2J5KGNyZWVwLCBtZXRyaWMpICU+JQogIG11dGF0ZSh2YWx1ZSA9IHZhbHVlL3ZhbHVlW3llYXIgPT0gMV0pICU+JQogIGdncGxvdChhZXMoeWVhcix2YWx1ZSwgY29sb3IgPSBtZXRyaWMsIGxpbmV0eXBlID0gbWV0cmljKSkgKwogIGdlb21fbGluZShzaXplID0gMS41KSArCiAgZmFjZXRfd3JhcCh+Y3JlZXApICsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICdQb3JjZW50YWplIGRlIG5pdmVsIGluaWNpYWwnLCBsYWJlbHMgPSBwZXJjZW50KSArIAogIHByZXNfdGhlbWUKCgoKYGBgCgpDb21vIHBvZGVtb3MgY29ycmVnaXIgZXN0bz8KCiMjIFVzYW5kbyBSZWdyZXNpw7NuIHBhcmEgRXN0YW5kYXJpemFjacOzbgoKRWwgcHJvY2VzbyBkZSAiZXN0YW5kYXJpemFjacOzbiIgbm9zIHB1ZWRlIGF5dWRhciBjb24gZXN0ZSBwcm9ibGVtYS4gCgpFbCBvYmpldGl2byBkZSBlc3RhbmRhcml6YWNpw7NuIGVzIGRlIGNvbnRyb2xhciBwb3IgZmFjdG9yZXMgY29tbyB0ZWNub2xvZ8OtYSBxdWUgcG9kcsOtYW4gY2VzZ2FyIENQVUUsIHkgc2FjYXIgbGEgc2XDsWFsIHZlcmRhZGVybyBkZSBsYSBwb2JsYWNpw7NuIGVuIENQVUUuCgpTdXBvbmdhIHF1ZSBubyBzYWJlbW9zIGV4YWN0YW1lbnRlIHF1ZSBlc3RhIHBhc2FuZG8gY29uIGVzdGUgY2FtYmlvIGRlIHRlY25vbG9nw61hLiAKClBlcm8sIHNhYmVtb3MgcXVlIGhheSBkb3MgZmxvdGFzLCB5IHVubyB0aWVuZSBtZWpvciB0ZWNub2xvZ8OtYSBkZWwgb3Ryby4gCgpQb2RlbW9zIGNvbnRyb2xhciBwb3IgZXN0bywgeSB2ZXIgcXVlIGVzdGUgcHJvY2VzbyBtZWpvcmEgbnVlc3RybyBoYWJpbGlkYWQgZGUgdXNhciBDUFVFIHBhcmUgbW9uaXRvcmVhciBiaW9tYXNhLgoKYGBge3J9CgpjcHVlX2V4YW1wbGUgPC0gY3B1ZV9leGFtcGxlICU+JQogIG11dGF0ZShsb2dfY3B1ZSA9IGxvZyhmbGVldF9jcHVlKSkKCm5vY3JlZXBfcmVnIDwtIGxtKGxvZ19jcHVlIH4gZmFjdG9yKHllYXIpLCBkYXRhID0gY3B1ZV9leGFtcGxlICU+JSBmaWx0ZXIoY3JlZXAgPT0gJ25vbmUnKSkKCmNyZWVwX3JlZyA8LSBsbShsb2dfY3B1ZSB+IGZhY3Rvcih5ZWFyKSAsIGRhdGEgPSBjcHVlX2V4YW1wbGUgJT4lIGZpbHRlcihjcmVlcCA9PSAnc29tZScpKQoKZnVsbF9jcmVlcF9yZWcgPC0gbG0obG9nX2NwdWUgfiBmYWN0b3IoeWVhcikgKyBmbGVldCwgZGF0YSA9IGNwdWVfZXhhbXBsZSAlPiUgZmlsdGVyKGNyZWVwID09ICdzb21lJykpCgoKIyBzdW1tYXJ5KG5vY3JlZXBfcmVnKQojCiMgc3VtbWFyeShjcmVlcF9yZWcpCiMKIyBzdW1tYXJ5KGZ1bGxfY3JlZXBfcmVnKQoKCnRlcm1mdW4gPC0gZnVuY3Rpb24oeCkgewoKICBvdXQgPSBhcy5udW1lcmljKHBhc3RlKHgsIGNvbGxhcHNlID0gJycpKQoKICByZXR1cm4ob3V0KQp9CgogIHRpZHlfbm9jcmVlcCA8LSB0aWR5KG5vY3JlZXBfcmVnKSAlPiUKICBtdXRhdGUoeWVhcl90ZXJtID0gc3RyX2RldGVjdCh0ZXJtLCd5ZWFyJykpICU+JQogIG11dGF0ZSh0cmFuc190ZXJtID0gZXhwKGVzdGltYXRlICsgc3RkLmVycm9yXjIvMikpICU+JQogIGZpbHRlcih5ZWFyX3Rlcm0gPT0gVCkgJT4lCiAgbXV0YXRlKHllYXIgPSBzdHJfZXh0cmFjdF9hbGwodGVybSwnW1xcZF0nLCBzaW1wbGlmeSA9IEYpLAogICAgICAgICB5ZWFyID0gbWFwX2RibCh5ZWFyLHRlcm1mdW4pKQoKCiAgdGlkeV9jcmVlcCA8LSB0aWR5KGNyZWVwX3JlZykgJT4lCiAgbXV0YXRlKHllYXJfdGVybSA9IHN0cl9kZXRlY3QodGVybSwneWVhcicpKSAlPiUKICBtdXRhdGUodHJhbnNfdGVybSA9IGV4cChlc3RpbWF0ZSArIHN0ZC5lcnJvcl4yLzIpKSAlPiUKICBmaWx0ZXIoeWVhcl90ZXJtID09IFQpICU+JQogIG11dGF0ZSh5ZWFyID0gc3RyX2V4dHJhY3RfYWxsKHRlcm0sJ1tcXGRdJywgc2ltcGxpZnkgPSBGKSwKICAgICAgICAgeWVhciA9IG1hcF9kYmwoeWVhcix0ZXJtZnVuKSkKCiAgIHRpZHlfZnVsbGNyZWVwIDwtICB0aWR5KGZ1bGxfY3JlZXBfcmVnKSAlPiUKICBtdXRhdGUoeWVhcl90ZXJtID0gc3RyX2RldGVjdCh0ZXJtLCd5ZWFyJykpICU+JQogIG11dGF0ZSh0cmFuc190ZXJtID0gZXhwKGVzdGltYXRlICsgc3RkLmVycm9yXjIvMikpICU+JQogIGZpbHRlcih5ZWFyX3Rlcm0gPT0gVCkgJT4lCiAgbXV0YXRlKHllYXIgPSBzdHJfZXh0cmFjdF9hbGwodGVybSwnW1xcZF0nLCBzaW1wbGlmeSA9IEYpLAogICAgICAgICB5ZWFyID0gbWFwX2RibCh5ZWFyLHRlcm1mdW4pLAogICAgICAgICByZWxfdHJhbnNfdGVybSA9IHRyYW5zX3Rlcm0vdHJhbnNfdGVybVt5ZWFyID09IG1pbih5ZWFyKV0pCgogY3B1ZV9leGFtcGxlICU+JQogICBmaWx0ZXIoY3JlZXAgPT0gJ3NvbWUnKSAlPiUKICBncm91cF9ieSh5ZWFyLCBjcmVlcCkgJT4lCiAgc3VtbWFyaXNlKGJpb21hc3MgPSBtZWFuKGIsIG5hLnJtID0gVCksIG1lYW5fY3B1ZSA9IG1lYW4oZmxlZXRfY3B1ZSwgbmEucm0gPSBUKSwKICAgICAgICAgICAgbWVkaWFuX2NwdWUgPSBtZWRpYW4oZmxlZXRfY3B1ZSwgbmEucm0gPSBUKSkgJT4lCiAgZ2F0aGVyKG1ldHJpYyx2YWx1ZSwgYmlvbWFzcyxtZWFuX2NwdWUpICU+JQogIGdyb3VwX2J5KGNyZWVwLCBtZXRyaWMpICU+JQogIG11dGF0ZSh2YWx1ZSA9IHZhbHVlL3ZhbHVlW3llYXIgPT0gMV0pICU+JQogIGdncGxvdChhZXMoeWVhcix2YWx1ZSkpICsKICBnZW9tX2xpbmUoc2l6ZSA9IDEuNSwgYWVzKGNvbG9yID0gbWV0cmljLCBsaW5ldHlwZSA9IG1ldHJpYykpICsKICBmYWNldF93cmFwKH5jcmVlcCkgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gJ1BvcmNlbnRhamUgZGUgbml2ZWwgaW5pY8OtYWwnLCBsYWJlbHMgPSBwZXJjZW50KSArCiAgIGdlb21fcG9pbnQoZGF0YSA9IHRpZHlfZnVsbGNyZWVwLCBhZXMoeWVhcixyZWxfdHJhbnNfdGVybSkpICsgCiAgIHByZXNfdGhlbWUKCgpgYGAKCk51ZXN0cm8gb2JqZXRpdm8gcG9yIGVsIHJlc3RvIGRlIGVzdGUgZWplcmNpY2lvIGVzIGFwcmVuZGVyIGNvbW8gaGFjZXIgZXN0byBub3NvdHJvcyBtaXNtb3MuCgojIFByaW1lcm9zIFBhc29zOiBEYXRvcyAiVGlkeSIgKE9yZGVuYWRvKQoKQW50ZXMgZGUgZXhwbG9yYXIgbG9zIGRhdG9zIGRlIENQVUUsIHZhbW9zIGEgYXByZW5kZXIgYWxndW5vcyBoZXJyYW1pZW50YXMgcGFyYSBvcmdhbml6YWNpw7NuIHkgdXNvIGRlIGRhdG9zLgoKTGEgY2FqYSBkZSBoZXJyYW1pZW50YXMgZXN0YSBjb250ZW5pZG8gZW4gYHRpZHl2ZXJzZWAKCmBgYAppbnN0YWxsLnBhY2thZ2VzKCd0aWR5dmVyc2UnKQpgYGAKCj4gQ2llbnTDrWZpY29zIGRlIGRhdG9zLCBzZWfDum4gZW50cmV2aXN0YXMgeSBlc3RpbWFjaW9uZXMgZXhwZXJ0b3MsIHBhc2VuIGVudHJlIDUwJS04MCUgZGUgc3UgdGllbXBvIGF0YXNjYWRvIGVuIGVsIHRyYWJham8gbXVuZGFubyBkZSBjb2xlY3RhbmRvIHkgcHJlcGFyYW5kbyBkYXRvcywgYW50ZXMgcXVlIHB1ZWRlIHNlciB1c2FkbyBwYXJhIHJlc3VsdGFkb3Mgw7p0aWxlcy4gLSBbTllUaW1lcyAoMjAxNCldKGh0dHA6Ly93d3cubnl0aW1lcy5jb20vMjAxNC8wOC8xOC90ZWNobm9sb2d5L2Zvci1iaWctZGF0YS1zY2llbnRpc3RzLWh1cmRsZS10by1pbnNpZ2h0cy1pcy1qYW5pdG9yLXdvcmsuaHRtbCkKCkVsIG9iamV0aXZvIGRlIGxvcyBoZXJyYW1pZW50YXMgcXVlIHZhbW9zIGEgaGFibGFyIGhveSBlcyBoYWNlciBlc3RlIHByb2Nlc28gbWFzIGbDoWNpbCB5IHLDoXBpZG8uCgojIyBSZWN1cnNvcwoKLSBbRGF0YSB3cmFuZ2xpbmcgY2hlYXRzaGVldCAoYGRwbHlyYCxgdGlkeXJgKV0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTUvMDIvZGF0YS13cmFuZ2xpbmctY2hlYXRzaGVldC5wZGYpCi0gW2dncGxvdCBjaGVhdHNoZWV0IChgZ2dwbG90YCldKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE1LzAzL2dncGxvdDItY2hlYXRzaGVldC5wZGYpCi0gW0RhdGEgd3JhbmdsaW5nIHdpdGggUiBhbmQgUlN0dWRpb10oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL3dlYmluYXJzL2RhdGEtd3JhbmdsaW5nLXdpdGgtci1hbmQtcnN0dWRpby8pCi0gW1IgZm9yIERhdGEgU2NpZW5jZV0oaHR0cDovL3I0ZHMuaGFkLmNvLm56LykKCgpQdWVkZXMgW2Rlc2NhcmdhciBSU3R1ZGlvXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9wcm9kdWN0cy9yc3R1ZGlvL2Rvd25sb2FkLykgc2kgbm8gbG8gdGllbmVzLCBvIHF1aWVyZXMgc2FjYXIgbGEgdmVyc2nDs24gbWFzIHJlY2llbnRlIGAwLjk5LjkwM2AgKG1lbnUgUlN0dWRpbyAtPiBBYm91dCBSU3R1ZGlvKSwgcXVlIHRpZW5lIG11Y2hvcyBoZXJyYW1pZW50YXMgw7p0aWxlcy4KClRhbWJpw6luLCBwdWVkZXMgc2FjYXIgbGEgdmVyc2nDs24gZGUgUiBtYXMgcmVjacOpbiBbYXF1w61dKGh0dHBzOi8vd3d3LnItcHJvamVjdC5vcmcvZm91bmRhdGlvbi8pLgoKIyMgUG9ycXVlIHVzYXIgYGRwbHlyYCB5IGB0aWR5cmA/CjEuICoqVmVsb2NpZGFkKioKMi4gKipMZWdpYmlsaWRhZCoqCjMuICoqSW50ZWdyYWNpw7NuIGNvbiBgZ2dwbG90MmAqKgoKCiMjIFF1ZSBlcyAidGlkeSBkYXRhIj8KCkVzdG8gZXMgZW4gZWplbXBsbyBjbMOhc2ljbyBkZSAibWVzc3kgZGF0YSI6CgpFcyDDunRpbCB5IGNsYXJvIChtYXMgbyBtZW5vcykgcGFyYSBzZXJlcyBodW1hbm9zCgpQZXJvLCBwYXJhIHVuYSBjb21wdXRhZG9yYSwgZXMgbXV5IGNvbmZ1c28KCiFbXShtZXNzX2RhdGEuanBnKQoKCiMjIFVzYW5kbyB0aWR5IGRhdGEKClZhbW9zIGEgYXByZW5kZXIgY29tbyB1c2FyIFIgcGFyYSBjb25zdHJ1aXIgZGF0b3MgdGlkeS4KCkRhdG9zIG9yZGVuYWRvcyB0aWVuZW4gdW5hIGZpbG9zb2bDrWEgYsOhc2ljbzoKCioqQ2FkYSBjb2x1bW5hIGVzIHVuIHRpcG8gZGUgZGF0b3MsIGNhZGEgZmlsYSBlcyB1bmEgb2JzZXJ2YWNpw7NuKioKCiFbXSh0aWR5LmpwZykKCkVzdG8gbm8gc3Vlbm8gbXV5IGNvbXBsaWNhZG8sIHBlcm8gZXMgbXV5IHBvZGVyb3NvCgpgYGB7cixlY2hvPVR9CgpwZWNlcyA8LSByZWFkLmNzdihmaWxlID0gJ2RhdGEvZGF0b3NfZGVzb3JkZW5hZG9zLmNzdicsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKYXNfZGF0YV9mcmFtZShwZWNlcykKYGBgCgpRdWUgc29uIGxvcyBwcm9ibGVtYXMgY29uIGVzdG9zIGRhdG9zPwoKVmFtb3MgYSB1c2FyIGFsZ3Vub3MgaGVycmFtaWVudGFzIHRlbCBwYXF1ZXRlIGBkcGx5cmAgcGFyYSByZXNvbHZlciBlc3RvLgoKUGFyYSBlbXBlemFyLCB2b3kgYSBpbnRyb2R1Y2lyIHR1IG51ZXZvIG1lam9yIGFtaWdvOiBlbCAicGlwZSIgKHR1Ym8pIG9wZXJhZG9yCgo8ZGl2IGNsYXNzPSJjZW50ZXJlZCI+CiAlPiUKPC9kaXY+CgpFbCB0dWJvIChgJT4lYCkgZXMgY29tbyB1biBjb3JyZWRvciBkZSByZWxhaXMKCiFbXShyZWxheS5qcGcpCgpMYSBpZGVhIGlzIHF1ZSB0b21hIGxhIGNvc2EgYSBsYSBpenF1aWVyZGEsIHkgbG8gcGFzYSBhIGxhIGRlcmVjaGEKClN1cG9uZ2EgcXVlIHRlbmVtb3MgdW5hIHZlY3RvciBkZSBkYXRvcyBgYSA9IGMoMSwyLDMpYAoKQ29tbyBvYnRlbmVtb3MgbGEgc3VtYSBkZSBgYWA/Cgpgc3VtKGEpYAoKYGBge3IsIGVjaG89VH0KCmEgPC0gYygxLDIsMykKCnN1bShhKQoKYGBgCgpQb2RlbW9zIHVzYXIgZWwgdHVibyBwYXJhIGVzdG8KCmBgYHtyLCBlY2hvPVR9CgphICU+JQogIHN1bSgpCgpgYGAKClRlbmVtb3MgYGFgLCBsbyBwYXNhbW9zIGEgYCU+JWAsIHkgYCU+JWAgbG8gcGFzYSBhIGBzdW1gCgpFc3RvIG5vIHBhcmVjZSBtdXkgw7p0aWwgZW4gZXN0ZSBjYXNlLCBwZXJvIHN1IHBvZGVyIGNyZWNlIG11eSByw6FwaWRhbWVudGUuCgogIDEuIEF5dWRhIG11Y2hvIGNvbiBsZWdpYmlsaWRhZAogIDIuIEF5dWRhIGNvbiBtZW1vcmlhCgpSZXRvcm5hbW9zIGEgbnVlc3Ryb3MgZGF0b3MgZGUgcGVzZXMuIE51ZXN0cm8gb2JqZXRpdm8gZXMgY29udmVydGlybG8gYSB1biBmb3JtYXRvIG9yZGVuYWRvLgoKYGBge3J9CgpwZWNlcwoKYGBgCgpgdGlkeXJgIHRpZW5lIHVuYSBmdW5jacOzbiBsbGFtYWRvIGBnYXRoZXJgIHF1ZSBub3MgYXl1ZGEgYXF1w60uCgohW10oZ2F0aGVyLmpwZykKCmBgYHtyLCBlY2hvPVR9CgpnYXRoZXIocGVjZXMsZXNwZWNpYSwgY2FwdHVyYSwgY29udGFpbnMoJ3BlcycpKQoKYGBgCgpWZW1vcyBxdWUgYGdhdGhlcmAgbm9zIGF5dWRvIGNyZWFyIHVuIGBkYXRhX2ZyYW1lYCB0aWR5LgoKUG9yIHF1ZSBlcyBtZWpvciBlc3RvPwoKUG9kZW1vcyBoYWNlciBlc3RlIG1pc21vIG9wZXJhY2nDs24gdXNhbmRvIHR1Ym9zCgpgYGB7ciwgZWNobyA9IFR9CgpwZWNlcyAlPiUKICBnYXRoZXIoZXNwZWNpYSxjYXB0dXJhLCBjb250YWlucygncGVzJykpCgpgYGAKWWEgcXVlIHRlbmVtb3MgZGF0b3Mgb3JkZW5hZG9zLCBwb2RlbW9zIGhhY2VyIG9wZXJhY2lvbmVzLgoKU3Vwb25nbyBxdWUgcXVlcmVtb3MgY2FwdHVyYSB0b3RhbCBzb2JyZSB0b2RvcyBlc3BlY2lhcyBlbiBjYWRhIGHDsW8uIENvbW8gaGFjZW1vcyBlc28/CgpgZHBseXJgIG5vcyBheXVkYSB0YW1iacOpbiEKCmBgYHtyfQoKcGVjZXMgJT4lCiAgZ2F0aGVyKGVzcGVjaWEsY2FwdHVyYSwgY29udGFpbnMoJ3BlcycpKSAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBzdW1tYXJpc2UoY2FwdHVyYV90b3RhbCA9IHN1bShjYXB0dXJhLCBuYS5ybSA9IFQpKQoKYGBgCgpRdWUgcGFzbyBhcXXDrT8KCmBncm91cF9ieWAgaGFjZSBncnVwb3MgZGUgbG9zIGRhdG9zLCB5IGhhY2Ugb3BlcmFjaW9uZXMgZW4gZXN0b3MgZ3J1cG9zLiBQb2RlbW9zIGFncmVnYXIgcG9yIGN1YWxxdWllciB0aXBvIGRlIGRhdG9zIGNhdGVnw7NyaWNvcwoKYHN1bW1hcml6ZWAgaGFjZSByZXPDum1lbmVzIGRlIGxvcyBkYXRvcyBlbiBjYWRhIGdydXBvLCB1c2FuZG8gY3VhbHF1aWVyIG9wZXJhY2nDs24gcXVlIHF1aWVyZXMuCgoKU3Vwb25nYSBxdWUgcXVlcmVtb3MgZWwgdG90YWwgeSBsYSBjYXB0dXJhIG3DoXhpbWEgZW4gY2FkYSBhw7FvLgoKYGBge3IsIGVjaG8gPSBUfQoKcGVjZXMgJT4lCiAgZ2F0aGVyKGVzcGVjaWEsY2FwdHVyYSwgY29udGFpbnMoJ3BlcycpKSAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBzdW1tYXJpc2UoY2FwdHVyYV90b3RhbCA9IHN1bShjYXB0dXJhKSwKICAgICAgICAgICAgbWF4aW1vX2NhcHR1cmEgPSBtYXgoY2FwdHVyYSkpCgoKYGBgCgpIYXkgZG9zIHRpcG9zIGdlbmVyYWxlcyBkZSBvcGVyYWNpb25lcyBlbiBgZHBseXJgCgpgc3VtbWFyaXNlYCBjb2xhcHNhIGRhdG9zCgpgbXV0YXRlYCBtb2RpZmljYSBkYXRvcwoKIVtdKHN1bV9tdXRhdGUucG5nKQoKU3Vwb25nYSBxdWUgcXVlcmVtb3MgY2FsY3VsYXIgZWwgcG9yY2VudGFqZSBkZSBjYXB0dXJhcyBwb3IgY2FkYSBlc3BlY2lhIGRlIHBlcyBlbiBjYWRhIGHDsW8uCgpQb2RlbW9zIHVzYXIgYG11dGF0ZWAgcGFyYSBlc3RvCgpgYGB7ciwgZWNobyA9IFR9CgpwZWNlcyAlPiUKICBnYXRoZXIoZXNwZWNpYSxjYXB0dXJhLCBjb250YWlucygncGVzJykpICU+JQogIGdyb3VwX2J5KHllYXIpICU+JQogIG11dGF0ZShjYXB0dXJhX2FubnVhbCA9IHN1bShjYXB0dXJhKSkgJT4lCiAgYXJyYW5nZSh5ZWFyKSAlPiUKICBtdXRhdGUocG9yY2VudGFqZSA9IGNhcHR1cmEgLyBjYXB0dXJhX2FubnVhbCkKCmBgYAoKWSBlbnRvbmNlcywgc3Vwb25nYSBxdWUgcXVlcmVtb3MgY2FsY3VsYXIgZWwgbcOtbmltbyBwb3JjZW50YWplIGRlIGFsZ8O6biBlc3BlY2lhIGVuIGNhZGEgYcOxbwoKCgpgYGB7cn0KCmZvbyA8LSBmdW5jdGlvbih4LHkpewoKICBvdXQgPSBtZWFuKGV4cCh4KSkKCiAgcmV0dXJuKG91dCkKfQoKIHBlY2VzICU+JQogIGdhdGhlcihlc3BlY2lhLGNhcHR1cmEsIGNvbnRhaW5zKCdwZXMnKSkgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgbXV0YXRlKGNhcHR1cmFfYW5udWFsID0gc3VtKGNhcHR1cmEpKSAlPiUKICBhcnJhbmdlKHllYXIpICU+JQogIG11dGF0ZShwb3JjZW50YWplID0gY2FwdHVyYSAvIGNhcHR1cmFfYW5udWFsKSAlPiUKICBzdW1tYXJpc2UobWluX3BvcmNlbnRhamUgPSBtaW4ocG9yY2VudGFqZSkpCgpgYGAKCkFob3JhLCB0YW1iacOpbiB0ZW5lbW9zIHVuIGJhc2UgZGUgZGF0b3MgZGUgdGVtcGVyYXR1cmEgZGUgYWd1YSBlbiBjYWRhIGHDsW8KClF1ZXJlbW9zIGV4cGxvcmFyIHNpIGFsZ8O6biByZWxhY2nDs24gZW50cmUgY2FwdHVyYXMgeSB0ZW1wZXJhdHVyYQoKQ29tbyBoYWNlbW9zIGVzbz8KCmBgYHtyfQoKCndhbGs8LXZlY3RvcihsZW5ndGggPSBsZW5ndGgodW5pcXVlKHBlY2VzJHllYXIpKSkKCiAgd2Fsa1sxXTwtIHJub3JtKDEsMCwuNzUpCgogIGZvciAoaSBpbiAyOmxlbmd0aCh1bmlxdWUocGVjZXMkeWVhcikpKQogIHsKICAgd2Fsa1tpXTwtICB3YWxrW2kgLSAxXSAqIDEgKyBybm9ybSgxLDAsMC43NSkKICB9CgoKICB3YXRlcl90ZW1wIDwtIGRhdGFfZnJhbWUoeWVhciA9IHVuaXF1ZShwZWNlcyR5ZWFyKSwgdGVtcGVyYXR1cmEgPSB3YWxrICsgMTggKQoKICB3YXRlcl90ZW1wICU+JQogICAgZ2dwbG90KGFlcyh5ZWFyLCB0ZW1wZXJhdHVyYSkpICsKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDIpICArCiAgICB4bGFiKCdBw7FvJykgKwogICAgeWxhYignVGVtcGVyYXR1cmEnKSArIAogICAgcHJlc190aGVtZQpgYGAKClVuIG9wY2nDs24gZXMgdXNhciBlbCBtYW5kbyBgbGVmdF9qb2luYAoKYGxlZnRfam9pbmAgdXNlIHVuIGNsYXZlIHBhcmEgaWRlbnRpZmljYXIgb2JzZXJ2YWNpb25lcyBjb211bmVzIGVuIGRpZmVyZW50ZXMgYmFzZXMgZGUgZGF0b3MsIHkgYXBlbmRhIGNhZGEgb2JzZXJ2YWNpw7NuIHF1ZSB0aWVuZSB1biBwYXJlamEgYSB0dSBiYXNlIGRlIGRhdG9zIG9yaWdpbmFsLgoKTWlyYW1vcwoKYGBge3IsIGVjaG8gPSBUfQoKIHBlY2VzICU+JQogIGdhdGhlcihlc3BlY2lhLGNhcHR1cmEsIGNvbnRhaW5zKCdwZXMnKSkgJT4lCiAgbGVmdF9qb2luKHdhdGVyX3RlbXAsIGJ5ID0gJ3llYXInKQoKYGBgCgpBaG9yYSwgcG9kZW1vcyBpbnZlc3RpZ2FyIG51ZXN0cm8gaGlww7N0ZXNpcyBxdWUgdGVtcGVyYXR1cmEgeSBjYXB0dXJhIHNvbiBjb3JyZWxhY2lvbmFkbwoKYGBge3IsIGVjaG8gPSBUfQoKIHBlY2VzICU+JQogIGdhdGhlcihlc3BlY2lhLGNhcHR1cmEsIGNvbnRhaW5zKCdwZXMnKSkgJT4lCiAgbGVmdF9qb2luKHdhdGVyX3RlbXAsIGJ5ID0gJ3llYXInKSAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBzdW1tYXJpc2UoY2FwdHVyYV90b3RhbCA9IHN1bShjYXB0dXJhKSwKICAgICAgICAgICAgdGVtcGVyYXR1cmEgPSBtZWFuKHRlbXBlcmF0dXJhKSkgJT4lCiAgZ2dwbG90KGFlcyhjYXB0dXJhX3RvdGFsLCB0ZW1wZXJhdHVyYSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWVhbih0ZW1wZXJhdHVyYSkpKSArIHByZXNfdGhlbWUKCmBgYAoKCkVzdG9zIGhlcnJhbWllbnRhcyBzb24gbGEgZnVuZGFjacOzbiBkZSBjw7NkaWdvIGVmaWNpZW50ZS4KCkRlc3B1w6lzIGRlIGVzdG8sIHZhbW9zIGEgdmVyIGNvbW8gY29ubmVjdG9yIGxvcyBtYW5pcHVsYWNpb25lcyBkZSBgZHBseXJgIGNvbiBsb3MgcG9kZXJlcyB2aXN1YWxlcyBkZSBgZ2dwbG90YAoKYGBge3J9CgpjYXB0dXJhcyA8LSByZWFkX2NzdihmaWxlID0gJ2RhdGEvY2FwdHVyYXMuY3N2JykKCmNhcHR1cmFzIDwtIGNhcHR1cmFzICAlPiUKICBnYXRoZXIoYDE5NTBgOmAyMDEyYCwga2V5ID0gJ3llYXInLCB2YWx1ZSA9ICdjYXB0dXJhcycpICU+JQogIG11dGF0ZShjYXB0dXJhcyA9IGFzLm51bWVyaWMoY2FwdHVyYXMpLAogICAgICAgICB5ZWFyID0gYXMubnVtZXJpYyh5ZWFyKSkKCmBgYAoKIyMgRWplcmNpY2lvcyBkZSBEYXRvcyAiVGlkeSIKCiogSW1wb3J0ZSBsb3MgZGF0b3MgImNhcHR1cmFzLmNzdiIgdXNhbmRvIGByZWFkX2NzdmAKKiBDb252aWVydGUgbG9zIGRhdG9zIGEgdW4gZm9ybWF0byAidGlkeSIKKiBFeGFtaW5lIGxvcyBkYXRvcywgcXVlIHRpcG8gZGUgZGF0b3Mgc29uIGNhZGEgY29sdW1uYT8KICAgICogRXhhbWluZSBsb3MgY2FwdHVyYXMgZW4gcGFydGljdWxhcgoqIFVzZSBgbXV0YXRlYCBwYXJhIGNvbnZlcnRpciBsb3MgY2FwdHVyYXMgYSBjYWxvcmVzIG51bcOpcmljb3MKICAgICogUXVlIHBhc28gYSBsb3MgdmFsb3JlcyBkZSBjYXB0dXJhPwoqIFVzZSBgZ3JvdXBfYnlgIHkgYHN1bW1hcml6ZWAgcGFyYSBjYWxjdWxhciBsYSBjYXB0dXJhIHRvdGFsIGVuIGNhZGEgYcOxbyBlbiBjYWRhIHBhw61zCiogVXNlIG90cm9zIGhlcnJhbWllbnRhcyBwYXJhIGV4cGxvcmFyIGxvcyBkYXRvcwoKIyBHcsOhZmljb3MgY29uIGBnZ3Bsb3RgCgpZYSBxdWUgaGVtb3MgdGVuaWRvIHVuIGludHJvZHVjY2nDs24gcsOhcGlkbyBhIGRhdG9zICJ0aWR5IiwgdmFtb3MgYSB2ZXIgY29tbyBwb2RlbW9zIHZpc3VhbGl6YXIgbnVlc3Ryb3MgcmVzdWx0YWRvcyB1c2FuZG8gdW4gcGFxdWV0ZSBwb2Rlcm9zbyBsbGFtYWRvIGBnZ3Bsb3QyYAoKRWwgImdnIiBlbiBgZ2dwbG90YCByZXByZXNlbnRhICJncmFtw6F0aWNhIGRlIGdyw6FmaWNvcyIKCkVzIHVuIGZvcm1hdG8gZGlzZcOxYWRvIHBhcmEgaGFibGFyIGNvbiBkYXRvcyB0aWR5IHBhcmEgaGFjZXIgZ3LDoWZpY29zIGJ1ZW5vcywgcmVwcm9kdWNpYmxlLCB5IGZsZXhpYmxlCgpObyBoYXkgbmFkYSBlbiBgZ2dwbG90YCBxdWUgbm8gc28gcHVlZGUgaGFjZXIgY29uIGxvcyBoZXJyYW1pZW50YXMgZ3LDoWZpY29zIGJhc2UgZGUgUi4gUG9yIGxvIG1pc21vLCBwdWVkZXMgaGFjZXIgdG9kb3MgbG9zIG9wZXJhY2lvbmVzIGRlIGRhdG9zIHRpZHkgZW4gYmFzZSBSIHRhbWJpw6luLgoKUGVybywgbGEgZmlsb3NvZsOtYSBkZSBlc3RvcyBwYXF1ZXRlcyBlcyBlc3RhbmRhcml6YXIgZWwgcHJvY2VzbyBwYXJhIGhhY2VyIGxvIG1hcyByw6FwaWRvLCBtYXMgdHJhbnNwYXJlbnRlLCB5IG1hcyByZXByb2R1Y2libGUuCgpFbXBlemFtb3Mgb24gZ3LDoWZpY29zIGRlIGBnZ3Bsb3RgLCBqdWdhbmRvIGNvbiBsb3MgZGF0b3MgZGUgY2FwdHVyYSBxdWUgY3JlZW1vcyBlbiBlbCB1bHRpbW8gcGFzby4KCgpgYGB7cn0KY2FwdHVyYXMKYGBgCgpRdWVyZW1vcyBoYWNlciB1biBncsOhZmljbyBkZSBjYXB0dXJhcyB0b3RhbGVzIHNvYnJlIHRpZW1wby4KClByaW1lcm8sIHBvZGVtb3MgdXNhciBgZHBseXJgIHBhcmEgc2FjYXIgZXN0b3MgZGF0b3MKCmBgYHtyLCBlY2hvID0gVH0KCmNhcHR1cmFzX3Bvcl90aWVtcG8gPC0gY2FwdHVyYXMgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgc3VtbWFyaXNlKGNhcHR1cmFzX3RvdGFsZXMgPSBzdW0oY2FwdHVyYXMsIG5hLnJtID0gVCkpCgpjYXB0dXJhc19wb3JfdGllbXBvCmBgYAoKQ29tbyBoYWNlbW9zIGVzdGUgZ3LDoWZpY28gbm9ybWFsbWVudGU/CgpgYGB7cn0KCnBsb3QoY2FwdHVyYXNfcG9yX3RpZW1wbyR5ZWFyLCBjYXB0dXJhc19wb3JfdGllbXBvJGNhcHR1cmFzX3RvdGFsZXMpCgpgYGAKCk5vIGhheSBuYWRhIG1hbG8gY29uIGVzdG8uIFBlcm8sIHZhbW9zIGEgdmVyIGNvbW8gaGFjZXIgZXN0byBjb24gYGdncGxvdGAKCkVtcGV6YW1vcyBjcmVhbmRvIHVuYSBjdWFkcmEgdmFjw61hCgpgYGB7ciwgZWNobyA9IFR9CgpnZ3Bsb3QoZGF0YSA9IGNhcHR1cmFzX3Bvcl90aWVtcG8sIGFlcyh4ID0geWVhciwgeSA9IGNhcHR1cmFzX3RvdGFsZXMpKQoKYGBgCgpFbCBtYW5kbyBgZ2dwbG90YCBjcmVlIG51ZXN0cm8gY3VhZHJvLCBkb25kZSB2YW1vcyBhIHBpbnRhciBjb24gZGlmZXJlbnRlcyBjYXBhcy4KCkHDsWFkaW1vcyBjYXBhcyB1c2FuZG8gZWwgc8OtbWJvbG8gYCtgLiBFc3RvIG5vcyBkaWNlIHF1ZSBxdWVyZW1vcyBhw7FhZGlyIHVuYSBjYXBhIGEgbnVlc3RybyBjdWFkcm8uCgpgYGB7cn0KCmdncGxvdChkYXRhID0gY2FwdHVyYXNfcG9yX3RpZW1wbywgYWVzKHggPSB5ZWFyLCB5ID0gY2FwdHVyYXNfdG90YWxlcykpICsKICBnZW9tX3BvaW50KCkKCgpgYGAKCllhIHRlbmVtb3MgYWxnbyBtdXkgc2ltaWxhciBhIGxvIHF1ZSB0ZW5lbW9zIHVzYW5kbyBgcGxvdGAsIHBlcm8gc2UgdmUgbXVjaG8gbWFzIGNvbmZ1bmRpZG8hCgpFbXBlemFtb3MgY29uIGV4cGxpY2FuZG8gbG9zIGNvbXBhcnRpbWllbnRvcyBhcXXDrQoKYGFlc2AgcmVwcmVzZW50YSBlc3TDqXRpY2EgKGFlc3RoZXRpY3MpLiBFc3RvIGVzIGRvbmUgcG9uZXMgbGFzIGNvc2FzIHF1ZSBxdWllcmVzIGRpYnVqYXIuIFB1ZWRlcyB2ZXIgcXVlIGVzdG8gZXMgbG8gbWlzbW8gcXVlIGBwbG90KHgseSlgCgpQb3IgcXVlIG5vIG5lY2VzaXRhcyB1c2FyIGAkYD8KCkVudG9uY2VzLAoKYGdncGxvdChkYXRhID0gY2FwdHVyYXNfcG9yX3RpZW1wbywgYWVzKHggPSB5ZWFyLCB5ID0gY2FwdHVyYXNfdG90YWxlcykpYAoKRGljZSB1c2EgbG9zIGRhdG9zIGBjYXB0dXJhc19wb3JfdGllbXBvYCwgeSBoYWNlIHVuIGdyw6FmaWNvIGNvbiBjb29yZGluYWRvcyBgeGAgZGUgInllYXIiIHkgY29vcmRpbmFkb3MgYHlgIGRlICJjYXB0dXJhc190b3RhbGVzIgoKYGdlb21fcG9pbnRgIGHDsWFkZSB1bmEgY2FwYSBkZSAic2NhdHRlcnBsb3QiIGEgbnVlc3RybyBjdWFkcm8KCmBnZW9tX2AgcmVwcmVzZW50YSBkaWZlcmVudGVzIHRpcG9zIGRlIGNhcGFzLCBwb3IgZWplbXBsbwoKYGdlb21fcG9pbnRgIGHDsWFkZSB1bmEgY2FwYSBkZSAic2NhdHRlcnBsb3QiIGEgbnVlc3RybyBjdWFkcm8KCmBnZW9tX2xpbmVgIGHDsWFkZSB1bmEgY2FwYSBkZSBsaW5lYSBhIG51ZXN0cm8gY3VhZHJvCgoKYGBge3IsIGVjaG8gPSBUfQoKZ2dwbG90KGRhdGEgPSBjYXB0dXJhc19wb3JfdGllbXBvLCBhZXMoeCA9IHllYXIsIHkgPSBjYXB0dXJhc190b3RhbGVzKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKCkgCgpgYGAKCkVzdG8gdGllbmUgYWxndW5vcyBhdmVudGFkYXMgc29icmUgZ3LDoWZpY29zIGLDoXNpY29zLgoKSGFjZSB0dSBjw7NkaWdvIG1hcyBsZWdpYmxlIHkgbGluZWFyCgpQZXJvLCBhIGVzdGUgcHVudG8gcGFyZWNlIG1hcyB0cmFiYWpvIHF1ZSBub21hcyB1c2FuZG8gYHBsb3RgCgpFbCBwb2RlciB2ZXJkYWRlcm8gZW1waWV6YSBjb20gbGEgaGFiaWxpZGFkIGRlIGbDoWNpbG1lbnRlIHZpc3VhbGl6YXIgZGlmZXJlbnRlcyBhdHJpYnV0b3MgZGUgbG9zIGRhdG9zLgoKU3Vwb25lIHF1ZSBxdWVyZW1vcyBoYWNlciBlbCB0YW1hw7FvIGRlIGxvcyBjw61yY3Vsb3MgcHJvcG9yY2lvbmFsIGEgbGEgY2FwdHVyYSB0b3RhbCBlbiBlc2UgYcOxbwoKYGBge3IsIGVjaG8gPSBUfQoKZ2dwbG90KGRhdGEgPSBjYXB0dXJhc19wb3JfdGllbXBvLCBhZXMoeCA9IHllYXIsIHkgPSBjYXB0dXJhc190b3RhbGVzKSkgKwogIGdlb21fcG9pbnQoYWVzKHNpemUgPSBjYXB0dXJhc190b3RhbGVzKSkgKwogIGdlb21fbGluZSgpCgpgYGAKClkgY29uIHNvbG8gZXNvLCBoZW1vcyBhw7FhZGlkbyB1biB2aXN1YWxpemFyb24geSB1bmEgbGV5ZW5kYSEKCmBnZ3Bsb3RgIGludGVycHJldGEgY29zYXMgYWRlbnRybyBkZSBgYWVzYCBjb21vIGF0cmlidXRvcyBxdWUgZGViZSBkYXIgbGV5ZW5kYS4gUHVlZGVzIHVzYXIgZGlmZXJlbnRlcyB0aXBvcyBkZSBjb3NhcywgaW5jbHV5ZW5kbyBwb3IgZWplbXBsbyB0YW1hw7FvLCBjb2xvciwgbyBmb3JtYS4KCk5vdGFzIHF1ZSBwdXNlIGVsIGNvbWFuZGEgYHNpemVgIGVuIGVsIGBhZXNgIGRlIGBnZW9tX3BvaW50YAoKUXVlIHBhc2Egc2kgbG8gcG9uZ28gZW4gYGdncGxvdGA/CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBjYXB0dXJhc19wb3JfdGllbXBvLCBhZXMoeCA9IHllYXIsIHkgPSBjYXB0dXJhc190b3RhbGVzLCBzaXplID0gY2FwdHVyYXNfdG90YWxlcykpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fbGluZSgpCmBgYAoKYGFlc2AgYWRlbnRybyBkZSBgZ2dwbG90YCBlcyBjb21wYXJ0aWRvIGNvbiB0b2RvcyBjYXBhcy4KCmBhZXNgIGFkZW50cm8gZGVuIHVuIGBnZW9tX2AgZXMgc29sbyBwYXJhIGVzZSBjYXBhCgpgYGB7ciwgZWNobyA9IFR9CgpnZ3Bsb3QoZGF0YSA9IGNhcHR1cmFzX3Bvcl90aWVtcG8sIGFlcyh4ID0geWVhciwgeSA9IGNhcHR1cmFzX3RvdGFsZXMsIGNvbG9yID0gY2FwdHVyYXNfdG90YWxlcykpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fbGluZSgpCmBgYAoKUXVlIHBhc2Egc2kgcG9uZW1vcyBjb2xvciBhZnVlcmEgZGUgYGFlc2A/IEVzdG8gZXMgY29tbyBoYWNlbW9zIGNvbG9yZXMgc2luIGFzb2NpYWNpb25lcwoKYGBge3IsIGVjaG8gPSBUfQoKZ2dwbG90KGRhdGEgPSBjYXB0dXJhc19wb3JfdGllbXBvLCBhZXMoeCA9IHllYXIsIHkgPSBjYXB0dXJhc190b3RhbGVzKSApICsKICBnZW9tX3BvaW50KGNvbG9yID0gJ3JlZCcpICsKICBnZW9tX2xpbmUoKQpgYGAKCgpUb2RhdsOtYSBoZW1vcyBub21hcyBhc2lkbyBkYXRvcyBtYXMgY2xhcm9zLiBDb21vIHBvZGVtb3MgdXNhciBgZ2dwbG90YCBwYXJhIHZlciBtYXMgaW5mb3JtYWNpw7NuPwoKUG9yIGVqZW1wbG8sIHF1ZXJlbW9zIHZlciBxdWUgdGlwb3MgZGUgZXNwZWNpYSBjb21wb25lbiBsb3MgY2FwdHVyYXMgdG90YWxlcy4gUmVncmVzYW1vcyBhIGBkcGx5cmAgcG9yIHVuIG1vbWVudG8KCmBgYHtyLCBlY2hvID0gVH0KCmNhcHR1cmFzX3Bvcl90aWVtcG8gPC0gY2FwdHVyYXMgJT4lCiAgZ3JvdXBfYnkoeWVhciwgYFRpcG8gZGUgRXNwZWNpYWApICU+JQogIHN1bW1hcmlzZShjYXB0dXJhc190b3RhbGVzID0gc3VtKGNhcHR1cmFzLCBuYS5ybSA9IFQpKQoKY2FwdHVyYXNfcG9yX3RpZW1wbwoKYGBgCgpRdWUgcGFzYSBzaSB0cmF0ZW1vcyBkZSB2aXN1YWxpemFyIGVzdG8/CgpgYGB7ciwgZWNobz1UfQoKY2FwdHVyYXNfcG9yX3RpZW1wbyAlPiUKICBnZ3Bsb3QoYWVzKHllYXIsY2FwdHVyYXNfdG90YWxlcykpICsKICBnZW9tX2xpbmUoKQpgYGAKUXVlIHBhc28/PwoKVGVuZW1vcyBvYnNlcnZhY2lvbmVzIHBhcmEgY2FkYSBlc3BlY2lhLCBwb2RlbW9zIHZpc3VhbGl6YXIgZXNvIHBvciBhc29jaWFuZG8gZWwgdGlwbyBkZSBlc3BlY2lhIGEgdW4gcGFyw6FtZXRybyBlc3TDqXRpY28sIGNvbW8gY29sb3IuCgpgYGB7ciwgZWNobyA9IFR9CgpjYXB0dXJhc19wb3JfdGllbXBvICU+JQogIGdncGxvdChhZXMoeWVhcixjYXB0dXJhc190b3RhbGVzKSkgKwogIGdlb21fbGluZShhZXMoY29sb3IgPSBgVGlwbyBkZSBFc3BlY2lhYCkpCgpgYGAKCmVzIHVuIHBvY28gY29uZnVzbywgcGVybyBwdWVkZXMgdmVyIGVsIHBvZGVyISBQYXJhIGhhY2VybG8gbWFzIGNsYXJvLCBwb2RlbW9zIHBvciBlamVtcGxvIGhhY2VyIHVuIGBiYXJwbG90YCBkZWwgdWx0aW1vIGHDsW8uCgpIYXkgbXVjaG8gYXF1w60sIHkgaGF5IGN1cnNvcyB5IGxpYnJvcywgcGVybyBwdWVkZXMgdmVyIGVsIHBvZGVyIGRlIHVzYW5kbyBkYXRvcyAidGlkeSIKY29uIGdncGxvdAoKYGBge3IsIGVjaG8gPSBUfQoKY2FwdHVyYXNfcG9yX3RpZW1wbyAlPiUKICBmaWx0ZXIoeWVhciA9PSBtYXgoeWVhcikpICU+JQogIG11dGF0ZShgVGlwbyBkZSBFc3BlY2lhYCA9IGZjdF9yZW9yZGVyKGBUaXBvIGRlIEVzcGVjaWFgLGNhcHR1cmFzX3RvdGFsZXMpKSAlPiUKICBnZ3Bsb3QoYWVzKGBUaXBvIGRlIEVzcGVjaWFgLGNhcHR1cmFzX3RvdGFsZXMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gJ2RvZGdlJykgKwogIGNvb3JkX2ZsaXAoKQoKYGBgCgojIyBFamVyY2ljaW9zIGRlIGBnZ3Bsb3RgCgoxLiBFeGFtaW5lIGxvcyBkYXRvcyBhZGVudHJvIGRlbCBiYXNlIGRlIGRhdG9zIGBkaWFtb25kc2AKMi4gSGFjZSB1biBncsOhZmljbyB1c2FuZG8gYHBsb3RgIGRlIGBwcmljZWAgKHByZWNpbykgY29tbyB1biBmdW5jacOzbiBkZSBgY2FyYXRgIChwZXNvKQozLiBIYWNlIGVsIG1pc21vIGdyw6FmaWNvIHVzYW5kbyBgZ2dwbG90YAo0LiBVc2UgYGdncGxvdGAgcGFyYSBoYWNlciBsb3MgcHVudG9zIGVsIGNvbG9yIHJvam8KNS4gVXNlIGBnZ3Bsb3RgIHBhcmEgaGFjZXIgbG9zIGNvbG9yZXMgcmVwcmVzZW50YXRpdm8gYSBgY29sb3JgCiAgICAqIFF1ZSBub3MgZGljZSBlc3RlIGluZm9ybWFjacOzbj8KNi4gSGFjZSBsb3MgY29sb3JlcyByZXByZXNlbnRhdGl2byBhIGBjb2xvcmAgeSBlbCBmb3JtYSByZXByZXNlbnRhdGl2byBkZSBgY3V0YAo3LiBKdWVnYSBjb24gZGlmZXJlbnRlcyBtYW5lcmFzIGRlIGV4cGxvcmFuZG8gbGEgcmVsYWNpw7NuIGVudHJlIHByZWNpbyB5IGNhcmFjdGVyw61zdGljYXMgZGUgbG9zIGRpYW1hbnRlcwoKIyBSZS1JbnRyb2R1Y2Npw7NuIGEgUmVncmVzacOzbgoKWWEgdGVuZW1vcyBhbGd1bm9zIGhlcnJhbWllbnRhcyBwYXJhIGF5dWRhcm5vcyBqdWdhciBjb24gZGF0b3MgZW4gUi4KCkFob3JhLCB2YW1vcyBhIHZlciBjb21vIHBvZGVtb3MgdXNhciBlc3RvcyBoZXJyYW1pZW50YXMganVudG9zIGNvbiByZWdyZXNpw7NuIHBhcmEgZXN0YW5kYXJpemFyIENQVUUKClBhcmEgZW1wZXphciwgdmFtb3MgYSBleHBsb3JhciByZWdyZXNpw7NuIGVuIFIuCgpSZWdyZXNpw7NuIGVzIHVuYSBmb3JtYSBkZSBtb2RlbG8sIHkgaGF5IGNsYXNlcyB5IGNsYXNlcyBlbiBlbCB0w7NwaWNvLCBxdWUgbm9zb3Ryb3MgdmFtb3MgYSB0cmF0YXIgZGUgZW50ZW5kZXIgZW4gbXV5IHBvY28gdGllbXBvLgoKQsOhc2ljYW1lbnRlLCB1bmEgcmVncmVzacOzbiBlcyB1biBtb2RlbG8gZGUgbGEgcmVsYWNpw7NuIGVudHJlIGRpZmVyZW50ZXMgdmFyaWFibGVzLgoKRW4gdGVvcsOtYToKCiQkIGVmZWN0byA9IGNhdXNhICsgZXJyb3IgICQkCgpNYXMgdMOpY25pY2FtZW50ZSwgZ2VuZXJhbG1lbnRlIGhhYmxhbW9zIGVuIHTDqXJtaW5vcyBkZSBkYXRvcyAiZGVwZW5kaWVudGVzIiAobGFzIGNvc2FzIHF1ZSBlc3RhbW9zIHRyYXRhbmRvIGRlIHByZWRlY2lyKSwgeSAiaW5kZXBlbmRpZW50ZXMiIChsYXMgY29zYXMgcXVlIHVzYW1vcyBwYXJhIHByZWRlY2lyKQoKJCQgZGVwZW5kaWVudGUgPSBpbmRlcGVuZGllbnRlICsgZXJyb3IgICQkCgoKVmFtb3MgYSBleHBsb3JhciBlc3RlIGlkZWEgdW4gcG9jbyBjb24gdW4gYmFzZSBkZSBkYXRvcyBzb2JyZSBlbCBwcmVjaW8geSBjYXJhY3RlcsOtc3RpY2FzIGRlIGRpYW1hbnRlcy4KCiFbXShkaWFtb2RzLmpwZykKCgpTdXBvbmUgcXVlIHF1ZXJlbW9zIGludmVydGlyIGVuIGRpYW1hbnRlcy4gVmFtb3MgYSB0cmF0YXIgZGUgdXNhciBlc3RvcyBkYXRvcyBwYXJhIGVudGVuZGVyIGxhIHJlbGFjacOzbiBlbnRyZSBwcmVjaW8gZGUgZGlhbWFudGVzIHkgc3VzIGNhcmFjdGVyw61zdGljYXMgdXNhbmRvIGxvcyBkYXRvcyBxdWUgZXhwbG9yYW1vcyBlbiBlbCBlamVyY2ljaW8gYW50ZXJpb3IuCgoKRW1wZXphbW9zIGNvbiB1bmEgY29zYSBiw6FzaWNvCgpgYGB7cn0KZGF0YSgiZGlhbW9uZHMiKQoKZGlhbWFudGVzIDwtIGRpYW1vbmRzICU+JQogIGFzX2RhdGFfZnJhbWUoKSAlPiUKICByZW5hbWUoZGVwdGhfcGVyYyA9IGRlcHRoKSAlPiUKICByZW5hbWUobGVuID0geCwgd2lkdGggPSB5LCBkZXB0aCA9IHopICU+JQogIG11dGF0ZShjdXQgPSBmYWN0b3IoY3V0LCBvcmRlcmVkID0gRikpCgpkaWFtYW50ZXMKYGBgCgpWYW1vcyBhIGVtcGV6YXIgcG9yIG1pcmFyIHVuYXMgcmVsYWNpb25lcywgZW1wZXphbmRvIGNvbiAiY2FyYXRzIiB5IHByZWNpbwoKYGBge3J9CgpkaWFtYW50ZXMgJT4lCiAgZ2dwbG90KGFlcyhjYXJhdCwgcHJpY2UpKSArCiAgZ2VvbV9wb2ludChhZXMoKSkgKwogIHlsYWIoJ1ByZWNpbycpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gZG9sbGFyKSArIAogIHByZXNfdGhlbWUKCmBgYAoKUG9kZW1vcyB2ZXIgcXVlIGhheSB1bmEgcmVsYWNpw7NuLCBwZXJvIG5vIGVzIHBlcmZlY3RvLiBDb21vIHBvZGVtb3Mgc2FjYXIgZXN0ZSByZWxhY2nDs24/CgpFbiBleGNlbCwgc2kgdGUgcmVjdWVyZGVzLCBwdWVkZXMgcG9uZXIgdW4gImZpdCBsaW5lIiwgcXVlIG5vcyBkYSBjb3NhcyBjb21vIGVsICRSXnsyfSQgeSBsYSBwZW5kaWVudGUgZGUgdW5hIGxpbmVhLiBQb2RlbW9zIHNhY2FyIGVsIG1pc21vIHByb2Nlc28gZW4gUiB1c2FuZG8gbGEgZnVuY2nDs24gYGxtYCAobGluZWFyIG1vZGVsKQoKYGxtYCB1cyBtdXkgZsOhY2lsIHVzYXIKCmBsbShkZXBlbmRpZW50ZSB+IGluZGVwZW5kaWVudGUsIGRhdGEgPSBkYXRvcylgLgoKUXVlIGRpY2UgZWwgbW9kZWxvIHNvYnJlIGxhIHJlbGFjacOzbiBlbnRyZSAiY2FyYXQiIChwZXNvKSB5IHByZWNpbz8KCmBgYHtyLCBlY2hvID0gVH0KcjEgPC0gbG0ocHJpY2UgfiBjYXJhdCwgZGF0YSA9IGRpYW1hbnRlcykKCnN1bW1hcnkocjEpCmBgYAoKIyMgRWplcmNpY2lvcyBkZSBSZWdyZXNpw7NuCgoqIENvbW8gaW50ZXJwcmV0YW1vcyBlbCBjb2VmaWNpZW50ZSBkZSBgY2FyYXRgPwoqIENvbW8gaW50ZXJwcmV0YW1vcyBlbCBgaW50ZXJjZXB0YD8KKiBDb21vIGludGVycHJldGFtb3MgZWwgYFItc3F1YXJlZGA/CgojIyBNYXMgUmVncmVzacOzbgoKVmVtb3MgcXVlIGVzdGUgbW9kZWxvCiAgICogbm9zIGRhIHVuYSBlc3RpbWFjacOzbiAic2lnbmlmaWNhbnRlIiBkZSBsYSByZWxhY2nDs24gZW50cmUgImNhcmF0IiB5IHByZWNpby4KICAgKiBUYW1iacOpbiwgdmVtb3MgcXVlIGVsIG1vZGVsbyBleHBsaWNhIH5gciAxMDAqcm91bmQoc3VtbWFyeShyMSkkci5zcXVhcmVkLDIpYCUgZGUgbGEgdmFyaWFjacOzbiBkZSBsb3MgZGF0b3MsIHF1ZSBlcyB1biBuaXZlbCBtdXkgYWx0bzogc2kgc2FiZW1vcyBsb3MgImNhcmF0cyIgZGUgdW4gZGlhbWFudGUsIHBvZGVtb3MgdXNhciBlc3RlIG1vZGVsbyBwYXJhIGhhY2VyIHVuIGJ1ZW4gZXN0aW1hY2nDs24gZGUgc3UgcHJlY2lvLgoKUGVybywgdGVuZW1vcyBtYXMgZGF0b3MsIHkgaGF5IHVuYSB0ZW9yw61hIHF1ZSBkaWNlIHF1ZSBkZWJlbW9zIGluY2x1aXIgdG9kb3MgdmFyaWFibGVzIHF1ZSBjcmVlbW9zIGFmZWN0YW4gbnVlc3RybyB2YXJpYWJsZSBkZXBlbmRpZW50ZS4gQ29tbyBwb2RlbW9zIHVzYXIgZXN0b3Mgb3Ryb3MgdmFyaWFibGVzPwoKVmVtb3MgcG9yIGVqZW1wbG8gcXVlIHRlbmVtb3MgdW4gdmFyaWFibGUgcXVlIHNlIGxsYW1hICJjdXQiLiBFc3RvIG5vcyBkaWNlIGxhIGNhbGlkYWQgZGUgbGEgY29ydGFkYSBkZSBlbCBkaWFtYW50ZS4gQ2xhcmFtZW50ZSwgZXN0byB2YSBhIGFmZWN0YXIgZWwgcHJlY2lvLiBQZXJvLCBjb21vIHBvZGVtb3MgaW5jbHVpciBlc3RlIHRpcG8gZGUgZGF0b3MgZW4gdW5hIHJlZ3Jlc2nDs24/CgpgYGB7cn0KZGlhbWFudGVzCmBgYAoKUiBub3MgZGVqYSB1c2FyIGVzdGUgdGlwbyBkZSBjb3NhLCB1c2FuZG8gdW4gY2xhc2UgZGUgdmFyaWFibGUgcXVlIHNlIGxsYW1hICJmYWN0b3JlcyIgKGZhY3RvcnMpLiBGYWN0b3JlcyBzb24gdmFyaWFibGVzIHF1ZSByZXByZXNlbnRhbiBncnVwb3MuIFNlIHB1ZWRlIGNyZWFyIGZhY3RvcmVzIGRlIGN1YWxxdWllciB0aXBvIGRlIGNvc2EsIHBlcm8gZ2VuZXJhbG1lbnRlIGVzIGNvc2FzIGNvbW8sIGNpdWRhZGVzLCBwYcOtcywgZXNwZWNpYSwgbyBlbiBlc3RlIGNhc28sIGNvcnRlLgoKCmBgYHtyLCBlY2hvPVR9Cgpjb3J0ZSA8LSBhcy5jaGFyYWN0ZXIoZGlhbWFudGVzJGN1dCkKCmhlYWQoY29ydGUpCgpgYGAKCkVuIGVzdGUgY2FzbywgdmVtb3MgdW5hIGN1ZXJkYSBub3JtYWwuIFBvZGVtb3MgY29udmVydGlyIGVuIHVuIGZhY3RvciB1c2FuZG8gYGFzLmZhY3RvcmAKCgoKYGBge3IsIGVjaG8gPSBUfQoKaGVhZChhcy5mYWN0b3IoY29ydGUpKQoKCmBgYAoKU2UgdmUgc2ltaWxhciwgcGVybyBhaG9yYSBlcyB1biBmYWN0b3IsIGNvbiBuaXZlbGVzIGNvcnJlc3BvbmRpZW5kbyBhIGNvcnRhLiBFcyBkZWNpciwgcXVlIGVzIGRhdGEgY2F0ZWfDs3JpY28uCgpFc3RvIGVzIMO6dGlsIHBvciBxdWUgYWhvcmEgcG9kZW1vcyB1c2FybG8gZW4gdW5hIHJlZ3Jlc2nDs24uCgpgYGB7cn0KCnJfZmFjdG9yIDwtIGxtKHByaWNlIH4gKGN1dCksIGRhdGEgPSBkaWFtYW50ZXMpCgpzdW1tYXJ5KHJfZmFjdG9yKQoKaGVhZChkaWFtYW50ZXMkY3V0KQoKYGBgCgpRdWUgcGFzbyBhICJmYWlyPyIgWSBjb21vIHBvZGVtb3MgdXNhciBjYXJhY3RlcmVzIGNvbW8gbsO6bWVyb3M/CgpSIGNyZWUgbG8gcXVlIGxsYW1hbW9zICJkdW1teSB2YXJpYWJsZXMiCgpgYGB7cn0KCmEgPSBkaWFnKDQpICU+JSBhc19kYXRhX2ZyYW1lKCkKCmNvbG5hbWVzKGEpIDwtIGMoJ0dvb2QnLCdWZXJ5IEdvb2QnLCdQcmVtaXVtJywnSWRlYWwnKQoKYSRjdXQgPSAgYygnR29vZCcsJ1ZlcnkgR29vZCcsJ1ByZW1pdW0nLCdJZGVhbCcpCgphCmBgYAoKUG9kZW1vcyBlbnRvbmNlcyB1c2FyIGRhdG9zIG51bcOpcmljb3MgKGUuZy4gY2FyYXQpIHkgY2F0ZWfDs3JpY28gKGUuZy4gY3V0KSBlbiBlbCBtaXNtbyBtb2RlbG8KCmBgYHtyLCBlY2hvID0gVH0KCnIyIDwtIGxtKHByaWNlIH4gY2FyYXQgKyBjdXQsIGRhdGEgPSBkaWFtYW50ZXMpCgpzdW1tYXJ5KHIyKQoKYGBgCgpDb21vIGludGVycHJldGFtb3MgZXN0ZSBtb2RlbG8/CgojIyBVc2FuZG8gUmVncmVzacOzbgoKQWhvcmEgdGVuZW1vcyBlc3RlIG1vZGVsbywgY29tbyBwb2RlbW9zIHVzYXJsbz8KClZhbW9zIGEgdXNhciBlbCBwYXF1ZXRlIGBicm9vbWAKCmBicm9vbWAgbm9zIGF5dWRhIHNhY2FyIGluZm9ybWFjacOzbiBpbXBvcnRhbnRlIHkgw7p0aWwgZGUgdW5hIHJlZ3Jlc2nDs24KCmBzdW1tYXJ5YCBub3MgZGEgbXVjaG8gaW5mb3JtYWNpw7NuLCBwZXJvIGVzIGRpZsOtY2lsIHVzYXIuCgoKUG9kZW1vcyB1c2FyIGBnbGFuY2VgIHBhcmEgc2FjYXIgcmVzdW1lcyBkZWwgbW9kZWxvCgpgYGB7ciwgZWNobz1UfQoKZ2xhbmNlKHIyKQoKYGBgCgpQb2RlbW9zIHVzYXIgYHRpZHlgIHBhcmEgdmVyIGxvcyBjb2VmaWNpZW50ZXMKCmBgYHtyLCBlY2hvID0gVH0KCnRpZHkocjIpCgpgYGAKQWhvcmEgdGVuZW1vcyBjb2VmaWNpZW50ZXMgZW4gdW5hIGZvcm1hIG11Y2hvIG1hcyDDunRpbCBwYXJhIG5vc290cm9zLgoKQ29tbyBpbnRlcnByZXRhbW9zIGxvcyBkaWZlcmVudGVzIHZhcmlhYmxlcyBhcXXDrT8KCk11Y2hhcyB2ZWNlcywgZWwgcHJvcMOzc2l0byBkZSB1biBtb2RlbG8gZXMgcGFyYSBoYWNlciBwcmVkaWNjaW9uZXMuIFBvZGVtb3MgaGFjZXIgZXN0byBjb24gbGEgZnVuY2nDs24gYHByZWRpY3RgCgpgYGB7ciwgZWNobyA9IFR9CgpoZWFkKHByZWRpY3QocjIpKQoKYGBgCgpgYGB7cn0KcGxvdChkaWFtYW50ZXMkcHJpY2UsIHByZWRpY3QocjIpKQpgYGAKCk90cm8gb3BjacOzbiB1cyB1c2FyIGxhIGZ1bmNpw7NuIGBhdWdtZW50YCBkZSBlbCBwYXF1ZXRlIGBicm9vbWAKCmBgYHtyLCBlY2hvID0gVH0KCmF1Z21lbnQocjIpCgpgYGAKCkVzdG8gZXMgdW4gcG9jbyBkaWbDrWNpbCBkZSBlbnRlbmRlciwgbG8gcXVlIGVzIG1hcyBpbXBvcnRhbnRlIGVzIGVsIHZhcmlhYmxlIGAuZml0dGVkYCwgcXVlIGNvbiBsb3MgdmFsb3JlcyBwcmV2aXN0by4KCiMjIERpYWduw7NzdGljb3MKCkNyZWFuZG8gdW4gcmVncmVzacOzbiBlcyBmYWNpbC4gRXN0ZSBlcyBidWVubyBwZXJvIHRhbWJpw6luIHRpZW5lIHJpZXNnb3MuCgpIYXkgY3Vyc29zIHkgbGlicm9zIGVuIGVzdGUgdMOzcGljbywgeSBubyBwb2RlbW9zIGN1YnJpciB0b2RvIGhveS4KClBlcm8sIHZhbW9zIGEgdmVyIGFsZ3Vub3MgZGlhZ25vc3RpY2FzIGLDoXNpY29zLgoKYGBge3IgZGlhZ25vc3RpY3N9CgpwbG90KHIyKQoKYGBgCgpZLCBwb2RlbW9zIHZlciBxdWUgaGF5IHByb2JsZW1hcy4gTm8gaW5kaXF1ZSBxdWUgbm8gcG9kZW1vcyB1c2FyIGVzdGUgbW9kZWxvLCBwZXJvIGRpY2UgcXVlIG5lY2VzaXRhbW9zIGludmVzdGlnYXIgbGFzIGNvc2FzIG1hcy4KClBhcmEgZGFyIG90cm8gZWplbXBsbywgdmFtb3MgYSB2ZXIgZGlhZ27Ds3N0aWNvcyBkZSBvdHJvIGJhc2UgZGUgZGF0b3Mgc29icmUgZmxvcmVzCgpgYGB7ciBjb3VudGVyZXhhbXBsZX0KCnJfaXJpcyA8LSBsbShTZXBhbC5MZW5ndGggfiAgUGV0YWwuTGVuZ3RoICsgUGV0YWwuV2lkdGggKyBTcGVjaWVzLCBkYXRhID0gaXJpcykKCnN1bW1hcnkocl9pcmlzKQoKcGxvdChyX2lyaXMpCgpgYGAKCkVuIGVzdGUgY2FzZSBsb3MgZGlhZ27Ds3N0aWNvcyBzb24gbXVjaG8gbWVqb3IuCgojIFVzYW5kbyBSZWdyZXNpw7NuIFBhcmEgQ1BVRQoKQWhvcmEgdGVuZW1vcyBsb3MgaGVycmFtaWVudGFzIHBhcmEgcmVncmVzYXIgYSBudWVzdHJvIHByZWd1bnRhIG9yaWdpbmFsOiBjb21vIHBvZGVtb3MgZXN0YW5kYXJpemFyIGxvcyB2YWxvcmVzIGRlIENQVUUgcGFyYSBDaGl0YT8KCkVtcGV6YW1vcyBjb24gbG9zIGRhdG9zCgpgYGB7ciwgZWNobz1UfQoKY2hpdGFfY3B1ZSA8LSByZWFkLmNzdihmaWxlID0gJ0NoaXRhX0NQVUVfMjAwMV8yMDE1LmNzdicsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSAlPiUKICBhc19kYXRhX2ZyYW1lKCkKCmNoaXRhX2NwdWUKCmBgYAoKVmVtb3MgcXVlIG5vIGhheSB1biB2YXJpYWJsZSBsbGFtYWRvIENQVUUsIHRlbmVtb3MgcXVlIGNyZWFybG8uCgpDb24gbG9zIHZhcmlhYmxlcyBxdWUgdGVuZW1vcywgcXVlIG9wY2lvbmVzIHRlbmVtb3MgcGFyYSBkZWZpbmlyIENQVUU/CgpcbgpcbgpcbgpcbgpgYGB7ciwgZWNobyA9IFR9CgpjaGl0YV9jcHVlIDwtIGNoaXRhX2NwdWUgJT4lCiAgbXV0YXRlKGNwdWUgPSBDaGl0YV9rZyAvIE5vX0Zpc2hlcnMpCgpgYGAKClZhbW9zIGEgZXhwbG9yYXIgbGEgaWRlYSByw6FwaWRhbWVudGUgYXF1w60sIHkgZW50b25jZXMgdHJhdGFyIGRlIGhhY2VyIGVzdG8gc29sby4KCkNyZWFtb3MgdW5hIHJlZ3Jlc2nDs24gY29uIHZhcmlhYmxlcyBkZXBlbmRpZW50ZSBgY3B1ZWAgeSB2YXJpYWJsZXMgZGVwZW5kaWVudGUgYERpc3RfY29hc3RgLCBgVmVzc2VsYCB5IGBHZWFyYC4gSW1wb3J0YW50ZW1lbnRlLCB0YW1iacOpbiB2YW1vcyBhIGluY2x1aXIgZWwgdmFyaWFibGUgYFllYXJgLgoKYFllYXJgIGlzIGxhIGNvc2EgbWFzIGltcG9ydGFudGUgYXF1w60uCgpSZWN1ZXJkYSBxdWUgbGEgaWRlYSBkZSBsb3MgY29lZmljaWVudGUgZW4gdW5hIHJlZ3Jlc2nDs24gZXMgcXVlIGVsIGNvZWZpY2llbnRlIGVzIGVsIGVmZWN0byBkZSBlc3RlIHZhcmlhYmxlIHNvYnJlIGVsIHZhcmlhYmxlIGRlcGVuZGllbnRlICoqY29udHJvbGFuZG8gcG9yIGxvcyBvdHJvcyB2YXJpYWJsZXMqKi4KCkVudG9uY2VzLCBlc3RvIGRpY2UgcXVlIGxvcyBjb2VmaWNpZW50ZXMgZGUgYFllYXJgIGRlYmUgc2VyIGVsIGVmZWN0byBkZSBhw7FvIHNvYnJlIENQVUUsIGNvbnRyb2xhbmRvIGVuIGVzdGUgY2FzbyBwb3IgY29zYXMgY29tbyBkaXN0YW5jaWEgZGUgbGEgY29zdGEuCgpSZWN1ZXJkYSBxdWUgZWwgb2JqZXRpdm8gZGUgZGUgZXN0YW5kYXJpemFjacOzbiBpcyBjcmVhciB1bmEgdGVuZGVuY2lhIGRlIGFidW5kYW5jaWEgaW1wYXJjaWFsLgoKRW50b25jZXMsIHBvZGVtb3MgaW50ZXJwcmV0YXIgbG9zIGNvZWZpY2llbnRlcyBkZSBgWWVhcmAgY29tbyBsYSB0ZW5kZW5jaWEgZGUgYWJ1bmRhbmNpYSwgY29udHJvbGFuZG8gcGFyYSBsb3Mgb3Ryb3MgdmFyaWFibGVzLgoKYGBge3IsIGVjaG89VH0KCmNwdWVfcmVnIDwtIGxtKGNwdWUgfiBmYWN0b3IoWWVhcikgKyBEaXN0X2NvYXN0ICsgVmVzc2VsICsgR2VhciwgZGF0YSA9IGNoaXRhX2NwdWUpCgpzdW1tYXJ5KGNwdWVfcmVnKQoKYGBgCgpWZW1vcyBxdWUgeWEgdGVuZW1vcyBsb3MgY29lZmljaWVudGVzIHBhcmEgYFllYXJgLiBUZXJtaW5hZG8/CgpWYW1vcyBhIHZlciBhbGd1bm9zIGRlIGxvcyBkaWFnbsOzc3RpY29zLgoKYGBge3J9CnBsb3QoY3B1ZV9yZWcpCmBgYAoKUXVlIGVzdGEgcGFzYW5kbyBhcXXDrT8KCmBgYHtyfQoKY3B1ZV9yZWcgJT4lCiAgYXVnbWVudCgpICU+JQogIGdncGxvdCgpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMCkpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoLmZpdHRlZCksIGJpbnMgPSA1MCwgYWxwaGEgPSAwLjc1KSArIAogIHByZXNfdGhlbWUKCmBgYAoKYGBge3J9CgpjcHVlX3JlZyAlPiUKICBhdWdtZW50KCkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAwKSkgKwogIGdlb21faGlzdG9ncmFtKGFlcygucmVzaWQpLCBhbHBoYSA9IDAuNzUpICsKICBzY2FsZV95X2xvZzEwKCkgKyAKICBwcmVzX3RoZW1lCgpgYGAKClZlbW9zIHF1ZSBsYSBkaXN0cmlidWNpw7NuIG5vIGVzIG5vcm1hbCEKCkNvcnJlZ2ltb3MgZXN0byB1c2FuZG8gbG9nYXJpdG1vcy4KCkxhIHByb2JsZW1hIGVzIHF1ZSBkYXRvcyBkZSBDUFVFIGdlbmVyYWxtZW50ZSBubyBzb24gbm9ybWFsbWVudGUgcmVwYXJ0aWRvLgoKU29uIGxvIHF1ZSBsbGFtYW1vcyB1biBkaXN0cmlidWNpw7NuICJsb2cgbm9ybWFsIgoKYGBge3J9Cgpub3JtX3ZhciA9IHJub3JtKDEwMDAsNSwxMCkKCmxub3JtX3ZhciA9IHJsbm9ybSgxMDAwLGxvZyg1KSwxKQoKCgogZGF0YV9mcmFtZSgnbm9ybWFsJyA9IG5vcm1fdmFyLCAnbG9nIG5vcm1hbCcgPSBsbm9ybV92YXIpICU+JQogIGdhdGhlcignRGlzdHJpYnVjacOzbicsJ3ZhbHVlJykgJT4lCiAgZ2dwbG90KGFlcyh2YWx1ZSwgZmlsbCA9IERpc3RyaWJ1Y2nDs24pKSArCiAgIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAwKSkgKwogIGdlb21faGlzdG9ncmFtKGNvbG9yID0gJ2JsYWNrJywgYmlucyA9IDUwKSArCiAgIGZhY2V0X2dyaWQoYERpc3RyaWJ1Y2nDs25gIH4uKSArIAogICBwcmVzX3RoZW1lCgoKCmBgYAoKRXN0b3MgZG9zIGRpc3RyaWJ1Y2lvbmVzIHRpZW5lbiBlbCBtaXNtbyBwcm9tZWRpbywgcGVybyBzZSB2ZW4gbXV5IGRpZmVyZW50ZS4gVmVzIHF1ZQoKICAqIExhIGRpc3RyaWJ1Y2nDs24gbm9ybWFsIGVzIHNpbcOpdHJpY28sIHkgcHVlZGUgdGVuZXIgdmFsb3JlcyBuZWdhdGl2bwogICogTGEgZGlzdHJpYnVjacOzbiBsb2ctbm9ybWFsIG5vIGVzIHNpbcOpdHJpY28sIHkgbm8gcHVlZGUgdGVuZXIgdmFsb3JlcyBuZWdhdGl2bwoKTWlyYW5kbyBsYSBoaXN0b2dyYW1hIGRlIGxvcyAicmVzaWR1YWxzIiwgdmVtb3MgcXVlIHNlIHBhcmVjZW4gbXVjaG8gbWFzICJsb2cgbm9ybWFsIgoKUmVjdWVyZGEgdGFtYmnDqW4gcXVlIGBsbWAgdGllbmUgbGEgc3Vwb3NpY2nDs24gcXVlIHR1cyBkYXRvcyBzb24gbm9ybWFsZXMsIHkgcG9kZW1vcyB2ZXIgcXVlIGxvcyBDUFVFJ3Mgbm8gc29uLgoKUG9kZW1vcyByZXNvbHZlciBlc3RvIHBvciBzdXBvc2ljacOzbiBsb3MgdmFsb3JlcyBkZSBgY3B1ZWAgYSBgbG9nKGNwdWUpYAoKYGBge3IsIGVjaG8gPSBUfQoKbG9nX2NwdWVfcmVnIDwtIGxtKGxvZyhjcHVlKSB+IGZhY3RvcihZZWFyKSArIERpc3RfY29hc3QgKyBWZXNzZWwgKyBHZWFyLCBkYXRhID0gY2hpdGFfY3B1ZSkKCmBgYAoKVmVtb3MgbG9zIGRpYWduw7NzdGljb3MKCmBgYHtyfQpsb2dfY3B1ZV9yZWcgJT4lCiAgYXVnbWVudCgpICU+JQogIGdncGxvdCgpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMCkpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoLnJlc2lkKSwgYWxwaGEgPSAwLjc1KSArIAogIHByZXNfdGhlbWUKYGBgCgpNdWNobyBtZWpvciEKCmBgYHtyfQpwbG90KGxvZ19jcHVlX3JlZykKYGBgCgpBaG9yYSwgcG9kZW1vcyB1c2FyIGVzdGUgbW9kZWxvIHBhcmEgc2FjYXIgbnVlc3RybyBlc3RhbmRhcml6YWRvIGVzdGltYWNpb25lcyBkZSBhYnVuZGFuY2lhLgoKVmFtb3MgYSBoYWNlciBlc3RvIHVzYW5kbyBgYnJvb21gIG90cmEgdmV6LgoKYGBge3IsIGVjaG8gPSBUfQoKY29lZnMgPC0gbG9nX2NwdWVfcmVnICU+JQogIHRpZHkoKQoKY29lZnMgPC0gY29lZnMgJT4lCiAgYmluZF9jb2xzKGNvbmZpbnRfdGlkeShsb2dfY3B1ZV9yZWcpKQpgYGAKCkNhc2kgZXN0YW1vcyBhbGzDrQoKVmVtb3MgcXVlIHRlbmVtb3MgY29lZmljaWVudGVzIHBhcmEgIlllYXIiLiBDb21vIHBvZGVtb3Mgc2FjYXIgbG8/CgpQb2RlbW9zIHVzYXIgZWwgbWFuZG8gYGZpbHRlcmAgKGZpbHRybykgcGFyYSBzYWNhciBmaWxhcyBxdWUgY29udGllbmVuICJZZWFyIgoKTGEgY29zYSBkaWbDrWNpbCBhcXXDrSBlcyBjb21vIGlkZW50aWZpY2FyIGN1YWxlcyBvYnNlcnZhY2lvbmVzIGNvbnRpZW5lbiAiWWVhciIKClBvZGVtb3MgaGFjZXIgZXN0byBtYW51YWxtZW50ZS4gTywgcG9kZW1vcyB1c2FyIHVuIG1hbmRvIHBhcmEgZW5jb250cmFyIHRvZG9zIGVzdG9zIGVudHJhZGFzLgoKYHN0cl9kZXRlY3RgIGRlIGVsIHBhcXVldGUgYHN0cmluZ3JgIG5vcyBwdWVkZSBheXVkYXIgYXF1w60uCgpgYGB7ciwgZWNobyA9IFR9CgphYnVuZGFuY2lhIDwtIGNvZWZzICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KHRlcm0sJ1llYXInKSkgJT4lCiAgICBtdXRhdGUoeWVhciA9IHN0cl9leHRyYWN0X2FsbCh0ZXJtLCdbXFxkXScsIHNpbXBsaWZ5ID0gRiksCiAgICAgICAgICB5ZWFyID0gbWFwX2RibCh5ZWFyLHRlcm1mdW4pKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKHRyYW5zX2VzdCA9IGV4cChlc3RpbWF0ZSArIHN0ZC5lcnJvcl4yLzIpLCB0cmFuc19sb3dlciA9IGV4cChjb25mLmxvdyArIHN0ZC5lcnJvcl4yLzIpLCB0cmFuc191cHBlciA9IGV4cChjb25mLmhpZ2ggKyBzdGQuZXJyb3JeMi8yICkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUocmVsX2VzdCA9IHRyYW5zX2VzdCAvIHRyYW5zX2VzdFt5ZWFyID09IG1pbih5ZWFyKV0gLSAxKQoKYWJ1bmRhbmNpYQoKYGBgCgpUaWVuZXMgcXVlIHRlbmVyIHByZWNhdWNpw7NuIGNvbiBlc3RvIHkgdmVyaWZpY2FyIHF1ZSBzb2xvIHNhY2FzdGUgbG8gcXVlIHF1aWVyZXMsIHBlcm8gZXMgdW4gaGVycmFtaWVudGEgcG9kZXJvc28uCgpZYSB0ZW5lbW9zIGluZGljZXMgZGUgImFidW5kYW5jaWEiLCByZWxhdGl2byBhIDIwMDEuIEVzdG9zIHZhbG9yZXMgc29uIG51ZXN0cm9zIGluZGljZXMgZXN0YW5kYXJpemFkbyBkZSBDUFVFLgoKYGBge3J9CgpjcHVlX3RyZW5kIDwtIGNoaXRhX2NwdWUgJT4lCiAgdW5ncm91cCgpICU+JQogIGdyb3VwX2J5KFllYXIpICU+JQogIHN1bW1hcmlzZShtZWRpYW5fY3B1ZSA9IG1lZGlhbihjcHVlLCBuYS5ybSA9IFQpKSAlPiUKICAgICAgICAgIG11dGF0ZSggIGRlbHRhX2NwdWUgPSBtZWRpYW5fY3B1ZSAvIG1lZGlhbl9jcHVlW1llYXIgPT0gbWluKFllYXIpICsgMV0gLSAxKQoKCmFidW5kYW5jaWEgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnRyYW5nZShhZXMoeWVhcixlc3RpbWF0ZSx5bWluID0gY29uZi5sb3csIHltYXggPSBjb25mLmhpZ2gpKSArCiAgeWxhYignQWJ1bmRhbmNpYScpICsKICB4bGFiKCdBw7FvJykgKyAKICBwcmVzX3RoZW1lCmBgYAoKCkhheSB1biBwYXNvIGZpbmFsLCBxdWUgZXMgdW4gcG9jbyBkaWbDrWNpbCBleHBsaWNhciwgcGVybyBlcyBpbXBvcnRhbnRlLgoKTG9zIGNvZWZpY2llbnRlcyBxdWUgaGVtb3MgZXN0aW1hZG8gc29uIGxlIHJlbGFjacOzbiBlbnRyZSBhw7FvIHkgbG9nKENQVUUpLgoKVGVuZW1vcyBxdWUgY29udmVydGlyIGVzdG9zIHZhbG9yZXMgYSBsYSByZWxhY2nDs24gZW50cmUgYcOxbyB5IENQVUUuCgpIYWNlbW9zIGVzdG8gcG9yIGxhIHJlbGFjacOzbgoKJCQgXGJldGFfe25vcm1hbH0gPSAgZXhwKFxiZXRhX3tsb2d9ICsgXGZyYWN7XHNpZ21hX3tcYmV0YX1eezJ9fXsyfSkgJCQKCkVuIGVzdGUgY2FzZSwgJFxiZXRhJCBlcyBsb3MgY29lZmljaWVudGVzIGRlIGHDsW8sIHkgJFxzaWdtYSQgZXMgZWwgYHN0ZC5lcnJvcmAgc2FjYWRvIHBvciBgdGlkeWAKCmBgYHtyLCBlY2hvID0gVH0KCmFidW5kYW5jaWEgJT4lCiAgc2VsZWN0KHRlcm0sIGVzdGltYXRlLCBzdGQuZXJyb3IsdHJhbnNfZXN0KQoKCmBgYAoKYGBge3IsIGVjaG8gPSBUfQphYnVuZGFuY2lhICU+JQogIG11dGF0ZSh5ZWFyID0gc3RyX2V4dHJhY3RfYWxsKHRlcm0sJ1tcXGRdJywgc2ltcGxpZnkgPSBGKSwKICAgICAgICAgIHllYXIgPSBtYXBfZGJsKHllYXIsdGVybWZ1bikpICU+JQogIGdncGxvdCgpICsKICBnZW9tX3BvaW50cmFuZ2UoYWVzKHllYXIsdHJhbnNfZXN0LHltaW4gPSB0cmFuc19sb3dlciwgeW1heCA9IHRyYW5zX3VwcGVyKSkgKwogIHlsYWIoJ0FidW5kYW5jaWEnKSArCiAgeGxhYignQcOxbycpICsgCiAgcHJlc190aGVtZQpgYGAKCmBgYHtyfQoKYWJ1bmRhbmNpYSAlPiUKICBtdXRhdGUoeWVhciA9IHN0cl9leHRyYWN0X2FsbCh0ZXJtLCdbXFxkXScsIHNpbXBsaWZ5ID0gRiksCiAgICAgICAgICB5ZWFyID0gbWFwX2RibCh5ZWFyLHRlcm1mdW4pKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGFlcyh5ZWFyLHJlbF9lc3QsIGNvbG9yID0gJ0VzdGFuZGFyaXphZG8nKSwgc2l6ZSA9IDIpICsKICBnZW9tX3BvaW50KCBkYXRhID0gY3B1ZV90cmVuZCwgYWVzKFllYXIsZGVsdGFfY3B1ZSwgY29sb3IgPSAnT2JzZXJ2YWRvJyksIHNpemUgPSAyKSArCiAgeWxhYignQWJ1bmRhbmNpYScpICsKICB4bGFiKCdBw7FvJykgKwogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICdGdWVudGUnKSArIAogIHByZXNfdGhlbWUKCgpgYGAKClkgcG9yIGZpbiEgVGVuZW1vcyB1biBpbmRpY2UgZGUgYWJ1bmRhbmNpYSBlc3RhbmRhcml6YWRvLgoKU2UgcXVlIGVzbyBmdWUgbXVjaG8uLi4gRW4gcmVzdW1lbgoKICAxLiBFeHBsb3JhciB0dXMgZGF0b3MKICAyLiBDYWxjdWxhIENQVUUKICAzLiBFc2NvZ2UgdHVzIHZhcmlhYmxlcyBpbmRlcGVuZGllbnRlcyAobG9zIGRlIGxhIG1hbm8gZGVyZWNoYSksICoqc2llbXByZSBpbmNsdXllbmRvIHRpZW1wbyBjb21vIHVuIHZhcmlhYmxlIGZpam8qKgogIDQuIENvbnZlcnRpciBDUFVFIGEgbG9nKENQVUUpCiAgNS4gdXNlIGBsbWAgbyBvdHJvIGZ1bmNpw7NuIHBhcmEgY3JlYXIgdW4gcmVncmVzacOzbiBkZSBgbG9nKGNwdWUpIH4gdmFyaWFibGVzIGRlcGVuZGllbnRlc2AKICA2LiBFeGFtaW5lIGxvcyBkaWFnbm9zdGljYXMgZGUgbGEgcmVncmVzacOzbiB1c2FuZG8gYHBsb3RgCiAgNy4gU2kgdG9kbyBwYXJlY2UgbWFzIG8gbWVub3MgYmllbiwgY29udGludWEuIFNpIHBhcmVjZSByYXJvLCBlbmN1ZW50cmEgdW4gZXN0YWTDrXN0aWNvCiAgOC4gU2FjYSBsb3MgY29lZmljaWVudGVzIGRlIHRpZW1wbyAoZS5nLiBhw7FvKQogIDkuIENvbnZlcnRpciBhIHVuaWRhZGVzIG5vcm1hbGVzIHBvciBsYSBmb3JtdWxhIGFudGVyaW9yCiAgMTAuIE1pcmEgbGEgdGVuZGVuY2lhIGdlbmVyYWRvCiAgMTEuIFRyYXRhIG90cm8gbW9kZWxvcywgY29tcGFyYSByZXN1bHRhZG9zLCBkaXNjdXRpcgoKIyBFamVyY2ljaW9zIEZpbmFsZXMKCiAgMS4gSW1wb3J0YSBsb3MgZGF0b3MgZGUgQ1BVRQogIDIuIEVzY29nZSBjb21vIHF1aWVyZXMgY2FsY3VsYXIgQ1BVRQogIDMuIEhhY2UgcmVzdW1lcywgZ3LDoWZpY29zIHkgdGFibGFzLCBkZSB2YXJpYWJsZXMgZGUgaW50ZXLDqXMKICA0LiBVc2UgdHVzIHZhcmlhYmxlcyBlc2NvZ2lkbyBlbiB1biByZWdyZXNpw7NuCiAgNS4gSW50ZXJwcmV0YXIgdG9kb3MgbG9zIGRpZmVyZW50ZXMgY29lZmljaWVudGUKICAgICAgKiBUaWVuZW4gc2VudGlkbyBsw7NnaWNvPwogIDYuIFNhY2EgbG9zIGluZGljZXMgZGUgYWJ1bmRhbmNpYXMgZXN0YW5kYXJpemFkbwogIDcuIEhhY2UgZ3LDoWZpY29zIGRlIGxvcyByZXN1bHRhZG9zCiAgOC4gQ29uc3RydXllIHBvciBsbyBtZW5vcyB1biBtb2RlbG8gYWx0ZXJuYXRpdm8gKHVzYW5kbyBvdHJvcyB2YXJpYWJsZXMgaW5kZXBlbmRpZW50ZXMpCiAgOS4gQ29tcGFyZSBsb3MgcmVzdWx0YWRvcwo=